package com.kym.service.miniapp.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
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.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.OrderUtils;
import com.kym.entity.admin.InvoiceDetail;
import com.kym.entity.admin.queryParams.InvoiceQueryParam;
import com.kym.entity.common.PageBean;
import com.kym.entity.miniapp.ChargeOrder;
import com.kym.entity.miniapp.Invoice;
import com.kym.entity.miniapp.InvoiceTitle;
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.FaPiao;
import com.kym.entity.wechat.InvoiceOrderDetail;
import com.kym.mapper.miniapp.InvoiceMapper;
import com.kym.service.admin.InvoiceDetailService;
import com.kym.service.miniapp.ChargeOrderService;
import com.kym.service.miniapp.DataDictService;
import com.kym.service.miniapp.HuapiaoerInvoiceService;
import com.kym.service.miniapp.InvoiceService;
import com.kym.service.miniapp.InvoiceTitleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * <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 HuapiaoerInvoiceService huapiaoerInvoiceService;
    private final DataDictService dataDictService;
    private final InvoiceDetailService invoiceDetailService;
    private final InvoiceTitleService invoiceTitleService;

    public InvoiceServiceImpl(ChargeOrderService chargeOrderService, HuapiaoerInvoiceService huapiaoerInvoiceService, DataDictService dataDictService, InvoiceDetailService invoiceDetailService, InvoiceTitleService invoiceTitleService) {
        this.dataDictService = dataDictService;
        this.chargeOrderService = chargeOrderService;
        this.huapiaoerInvoiceService = huapiaoerInvoiceService;
        this.invoiceDetailService = invoiceDetailService;
        this.invoiceTitleService = invoiceTitleService;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Invoice applyInvoice(ApplyInvoiceParams params) {
        if (CommUtil.isEmptyOrNull(params.getStartChargeSeqs())) {
            throw new BusinessException("请选择需要开票的订单");
        }
        var userId = StpUtil.getLoginIdAsLong();
        var orders = chargeOrderService.getChargeOrdersBySeqs(params.getStartChargeSeqs());

        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();

        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("订单总金额或总电费或总服务费金额异常");
        }
        if (params.getEmail() == null || params.getEmail().isBlank()) {
            throw new BusinessException("请填写接收发票的邮箱地址");
        }
        if (!params.getEmail().matches("^[\\w.%+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$")) {
            throw new BusinessException("邮箱格式不正确");
        }

        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())
                // 用户申请时直接保存抬头信息
                .setEmail(params.getEmail())
                .setPhone(params.getPhone())
                .setInvoiceType(params.getInvoiceType())
                .setInvoiceTitle(params.getInvoiceTitle())
                .setTaxId(params.getTaxId())
                .setAddress(params.getAddress())
                .setBankName(params.getBankName())
                .setBankAccount(params.getBankAccount())
                .setRemark(params.getRemark());
        baseMapper.insert(invoice);

        // 自动保存抬头信息
        if (params.getInvoiceTitle() != null && !params.getInvoiceTitle().isBlank()) {
            var exists = invoiceTitleService.lambdaQuery()
                    .eq(InvoiceTitle::getUserId, userId)
                    .eq(params.getInvoiceType() != null, InvoiceTitle::getInvoiceType, params.getInvoiceType())
                    .eq(Invoice.TYPE_企业.equals(params.getInvoiceType()), InvoiceTitle::getTaxId, params.getTaxId())
                    .eq(Invoice.TYPE_个人.equals(params.getInvoiceType()), InvoiceTitle::getInvoiceTitle, params.getInvoiceTitle())
                    .exists();
            if (!exists) {
                var title = new InvoiceTitle()
                        .setUserId(userId)
                        .setEmail(params.getEmail())
                        .setTelephone(params.getPhone())
                        .setInvoiceType(params.getInvoiceType())
                        .setInvoiceTitle(params.getInvoiceTitle())
                        .setTaxId(params.getTaxId())
                        .setAddress(params.getAddress())
                        .setBankName(params.getBankName())
                        .setBankAccount(params.getBankAccount());
                invoiceTitleService.saveTitle(title);
            }
        }

        chargeOrderService.lambdaUpdate().set(ChargeOrder::getInvoiceStatus, ChargeOrder.INVOICE_STATUS_开票中)
                .in(ChargeOrder::getStartChargeSeq, Arrays.stream(params.getStartChargeSeqs()).toList()).update();

        return 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)
                .select(InvoiceDetail::getFapiaoTime)
                .leftJoin(User.class, User::getId, Invoice::getUserId)
                .leftJoin(InvoiceDetail.class, InvoiceDetail::getApplyId, Invoice::getApplyId)
                .gt(CommUtil.isNotEmptyAndNull(params.getStartTime()), InvoiceDetail::getFapiaoTime, params.getStartTime())
                .lt(CommUtil.isNotEmptyAndNull(params.getEndTime()), InvoiceDetail::getFapiaoTime, params.getEndTime())
                .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)
                .in(Invoice::getStatus, List.of(Invoice.STATUS_待开票, 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();
        }
    }

    @Override
    @DSTransactional
    public void confirmManualInvoice(Long invoiceId) {
        var invoice = lambdaQuery().eq(Invoice::getId, invoiceId).one();
        if (invoice == null) {
            throw new BusinessException("发票记录不存在");
        }
        if (!Arrays.asList(Invoice.STATUS_开票中, Invoice.STATUS_待开票).contains(invoice.getStatus())) {
            throw new BusinessException("仅待开票或开票中状态的发票可确认手动开票");
        }

        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();

        // 写入发票详情记录
        var invoiceDetail = new InvoiceDetail()
                .setApplyId(invoice.getApplyId())
                .setFapiaoTime(LocalDateTime.now())
                .setStatus("ISSUED")
                .setTotalAmount(invoice.getInvoiceAmount())
                .setBuyerInformation(new FaPiao.BuyerInformation()
                        .setType(invoice.getInvoiceType())
                        .setName(invoice.getInvoiceTitle())
                        .setTaxpayer_id(invoice.getTaxId())
                        .setAddress(invoice.getAddress())
                        .setTelephone(invoice.getTelephone())
                        .setBank_name(invoice.getBankName())
                        .setBank_account(invoice.getBankAccount())
                        .setPhone(invoice.getPhone())
                        .setEmail(invoice.getEmail()));
        DynamicDataSourceContextHolder.push("db-admin");
        invoiceDetailService.createManualInvoiceDetail(invoiceDetail);
        DynamicDataSourceContextHolder.poll();

        log.info("手动开票确认完成, invoiceId:{}, applyId:{}, 关联订单:{}", invoiceId, invoice.getApplyId(), startChargeSeqs);
    }

}
