package com.kym.service.miniapp.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.github.pagehelper.PageHelper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.kym.common.IQuery;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.OrderUtils;
import com.kym.entity.admin.queryParams.InvoiceQueryParam;
import com.kym.entity.common.PageBean;
import com.kym.entity.miniapp.ChargeOrder;
import com.kym.entity.miniapp.DataDict;
import com.kym.entity.miniapp.Invoice;
import com.kym.entity.miniapp.User;
import com.kym.entity.miniapp.queryParams.ApplyInvoiceParams;
import com.kym.entity.miniapp.vo.InvoiceVo;
import com.kym.entity.wechat.InvoiceOrderDetail;
import com.kym.entity.wechat.TitleUrl;
import com.kym.mapper.miniapp.InvoiceMapper;
import com.kym.service.miniapp.ChargeOrderService;
import com.kym.service.miniapp.DataDictService;
import com.kym.service.miniapp.InvoiceService;
import com.kym.service.wechat.WxPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 发票记录表 服务实现类
 * </p>
 *
 * @author skyline
 * @since 2023-09-15
 */
@Service
@Slf4j
@DS("db-miniapp")
public class InvoiceServiceImpl extends MPJBaseServiceImpl<InvoiceMapper, Invoice> implements InvoiceService {

    private final ChargeOrderService chargeOrderService;
    private final WxPayService wxPayService;
    private final DataDictService dataDictService;

    public InvoiceServiceImpl(ChargeOrderService chargeOrderService, @Lazy WxPayService wxPayService, @Lazy DataDictService dataDictService) {
        this.dataDictService = dataDictService;
        this.chargeOrderService = chargeOrderService;
        this.wxPayService = wxPayService;
    }


    /**
     * 申请发票
     * 业务流程：
     * 1.校验订单
     * 2.数据库写入invoice对象
     * 3.api获取填写抬头的链接给前端
     * 4.前端打开上一步返回的填写链接，客户填写抬头信息
     * 5.接收抬头填写完成的通知，将抬头信息更新至第2步写入的invoice数据中
     * 6.运营系统财务人员审核之后确认开票，请求api进行电子发票开票操作
     *
     * @param params
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TitleUrl applyInvoice(ApplyInvoiceParams params) {
        if (CommUtil.isEmptyOrNull(params.getStartChargeSeqs())) {
            throw new BusinessException("请选择需要开票的订单");
        }
        // orderDetails 获取申请开票的订单
        var userId = StpUtil.getLoginIdAsLong();
        var orders = chargeOrderService.getChargeOrdersBySeqs(params.getStartChargeSeqs());

        // 校验未完结的订单 orderStatus = 0
        if (orders.stream().anyMatch(o -> o.getOrderStatus().equals(ChargeOrder.ORDER_STATUS_未知))) {
            log.error("存在未知状态的订单:{}", orders.stream().filter(o -> o.getOrderStatus().equals(ChargeOrder.ORDER_STATUS_未知)).map(ChargeOrder::getStartChargeSeq).toArray());
            throw new BusinessException("请勿勾选未完结的订单");
        }

        // 校验订单是已开票或者开票中
        var invoiced = orders.stream().filter(o -> o.getInvoiceStatus().equals(ChargeOrder.INVOICE_STATUS_开票中) || o.getInvoiceStatus().equals(ChargeOrder.INVOICE_STATUS_已开票)).toList();
        if (!invoiced.isEmpty()) {
            log.error("存在已开票或者开票中的订单:{}", invoiced.stream().map(ChargeOrder::getStartChargeSeq).toArray());
            throw new BusinessException("存在已开票或开票中的订单");
        }

        var orderDetails = orders.stream().map(item -> new InvoiceOrderDetail()
                        .setStartChargeSeq(item.getStartChargeSeq())
                        .setTotalPower(item.getTotalPower()).setTotalMoney(item.getTotalMoney())
                        .setElecMoney(item.getElecMoney()).setServiceMoney(item.getServiceMoney())
                        .setServiceMoneyDiscount(item.getServiceMoneyDiscount()))
                .toList();

        // 校验开票金额大于0
        var totalPayAmount = orders.stream().mapToInt(ChargeOrder::getPayAmount).sum();
        var elecMoney = orders.stream().mapToInt(ChargeOrder::getElecMoney).sum();
        var serviceMoney = orders.stream().mapToInt(ChargeOrder::getServiceMoney).sum();
        if (!(totalPayAmount > 0 && elecMoney > 0 && serviceMoney > 0)) {
            throw new BusinessException("订单总金额或总电费或总服务费金额异常");
        }
        // 组装invoice
        var invoice = new Invoice()
                .setUserId(userId)
                .setOpenid(StpUtil.getSession().getString("openid"))
                .setApplyId(OrderUtils.getOrderNo())
                .setOrderDetails(orderDetails) // 订单信息
                .setInvoiceAmount(totalPayAmount)   // 发票金额
                .setTotalMoney(elecMoney + serviceMoney) // 订单总金额
                .setElecMoney(elecMoney)    // 电费
                .setServiceMoney(serviceMoney)    // 服务费
                .setTotalPower(orders.stream().mapToDouble(ChargeOrder::getTotalPower).sum())   // 总电量
                .setServiceMoneyDiscount(orders.stream().mapToInt(ChargeOrder::getServiceMoneyDiscount).sum()); //总优惠服务费
        baseMapper.insert(invoice);

        // 更新订单开票状态为开票中
        chargeOrderService.lambdaUpdate().set(ChargeOrder::getInvoiceStatus, ChargeOrder.INVOICE_STATUS_开票中)
                .in(ChargeOrder::getStartChargeSeq, Arrays.stream(params.getStartChargeSeqs()).toList()).update();

        return wxPayService.titleUrl(invoice);

    }

    @Override
    public PageBean<InvoiceVo> listInvoice(InvoiceQueryParam params) {
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        MPJLambdaWrapper<Invoice> wrapper = JoinWrappers.lambda(Invoice.class)
                .selectAsClass(Invoice.class, InvoiceVo.class)
                .selectAll(Invoice.class)
                .select(User::getMobilePhone)
                .leftJoin(User.class, User::getId, Invoice::getUserId)
                .like(CommUtil.isNotEmptyAndNull(params.getPhone()), User::getMobilePhone, params.getPhone())
                .like(CommUtil.isNotEmptyAndNull(params.getInvoiceTitle()), Invoice::getInvoiceTitle, params.getInvoiceTitle())
                .like(CommUtil.isNotEmptyAndNull(params.getBiller()), Invoice::getBiller, params.getBiller())
                .like(CommUtil.isNotEmptyAndNull(params.getTaxId()), Invoice::getTaxId, params.getTaxId())
                .like(CommUtil.isNotEmptyAndNull(params.getInvoiceType()), Invoice::getInvoiceType, params.getInvoiceType())
                .like(CommUtil.isNotEmptyAndNull(params.getEmail()), Invoice::getEmail, params.getEmail())
                .eq(params.getStatus() != null, Invoice::getStatus, params.getStatus())
                .orderByDesc(Invoice::getId);
        var list = selectJoinList(InvoiceVo.class, wrapper);
        return new PageBean<>(list);
    }

    @Override
    public List<Invoice> listInvoiceForApp(Integer status) {
        return lambdaQuery().eq(Invoice::getUserId, StpUtil.getLoginIdAsLong()).eq(status != null, Invoice::getStatus, status).list();
    }

    @Override
    @Transactional
    public void cancelApplyInvoice(String invoiceId) {
        var invoice = lambdaQuery().eq(Invoice::getId, invoiceId).eq(Invoice::getStatus, Invoice.STATUS_待开票).orderByDesc(Invoice::getId).one();
        if (!CommUtil.isEmptyOrNull(invoice)) {
            // 订单发票状态修改为待开票
            var startChargeSeqs = invoice.getOrderDetails().stream().map(InvoiceOrderDetail::getStartChargeSeq).toList();
            chargeOrderService.lambdaUpdate().set(ChargeOrder::getInvoiceStatus, ChargeOrder.INVOICE_STATUS_待开票).in(ChargeOrder::getStartChargeSeq, startChargeSeqs).update();
            // 发票状态已作废
            lambdaUpdate().set(Invoice::getStatus, Invoice.STATUS_已作废).eq(Invoice::getId, invoice.getId()).update();
        }
    }

}
