package com.yc.ship.module.trade.service.invoice.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yc.ship.framework.mybatis.core.util.MyBatisUtils;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.trade.api.invoice.dto.InvoicingReqDTO;
import com.yc.ship.module.trade.controller.admin.invoice.vo.InvoicePageReqVO;
import com.yc.ship.module.trade.controller.admin.invoice.vo.InvoiceRespVO;
import com.yc.ship.module.trade.dal.dataobject.invoice.InvoiceDO;
import com.yc.ship.module.trade.dal.dataobject.invoice.InvoiceDetailDO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeOrderDO;
import com.yc.ship.module.trade.dal.mysql.invoice.InvoiceMapper;
import com.yc.ship.module.trade.enums.SellMethodEnum;
import com.yc.ship.module.trade.enums.invoice.InvoiceStatusEnum;
import com.yc.ship.module.trade.service.invoice.InvoiceDetailService;
import com.yc.ship.module.trade.service.invoice.InvoiceGroupService;
import com.yc.ship.module.trade.service.invoice.InvoiceHangxinService;
import com.yc.ship.module.trade.service.invoice.InvoiceService;
import com.yc.ship.module.trade.service.order.TradeOrderRepositoryService;
import com.yc.ship.module.trade.service.refund.TradeRefundRepositoryService;
import com.yc.ship.module.trade.utils.AgencyAuthUtils;
import com.yc.ship.module.trade.utils.ThreadLocalUtil;
import com.yc.ship.module.trade.utils.TradeUUCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.yc.ship.module.trade.enums.ApiConstants.ORDER_CHANGE_TYPE_OLD;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.*;


/**
 * 电子发票 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class InvoiceServiceImpl implements InvoiceService {

    @Resource
    private InvoiceMapper invoiceMapper;
    @Resource
    private InvoiceGroupService invoiceGroupService;
    @Resource
    private InvoiceHangxinService invoiceHangxinService;

    //TODO 待完善
    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    private TradeUUCodeUtils tradeUUCodeUtils;
    @Resource
    private ConfigApi configApi;
    @Resource
    private InvoiceDetailService invoiceDetailService;
    //TODO 待完善
    @Resource
    private TradeRefundRepositoryService tradeRefundRepositoryService;

    /**
     * 流水号前缀
     */
    private final static String INVOICE_PREFIX = "IVE";
    @Autowired
    private AgencyAuthUtils agencyAuthUtils;

    //TODO
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<InvoiceDO> applyInvoicing(InvoicingReqDTO reqDTO) {
        BigDecimal amt = BigDecimal.ZERO;
        List<TradeOrderDO> orderList = new ArrayList<>();
        if(reqDTO.getIsPlat()!=null && reqDTO.getIsPlat()==1){
            if(reqDTO.getTotalPrice().compareTo(BigDecimal.ZERO)<=0){
                throw exception0(500, "订单开票失败，开票金额不能为"+reqDTO.getTotalPrice());
            }
        }
        //查询订单及金额
        for (String orderNo : reqDTO.getOrderIdList()) {
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByNo(orderNo);
            Asserts.isTrue(null != order, INVOICING_ORDER_NOT_EXISTS.getCode(), "订单{}不存在", orderNo);
            if (order.getIsInvoice() != null && ObjectUtil.equals(order.getIsInvoice(), InvoiceStatusEnum.INVOICE_INVOIING.getValue())) {
                throw exception0(500, "订单{}开票中", orderNo);
            }
            if (order.getIsInvoice() != null && ObjectUtil.equals(order.getIsInvoice(), InvoiceStatusEnum.INVOICE_SUCCESS.getValue())) {
                throw exception0(500, "订单{}已开票", orderNo);
            }
            Asserts.isTrue(ObjectUtil.equals(order.getIsInvoice(), InvoiceStatusEnum.NOT_INVOICED.getValue())
                    || ObjectUtil.equals(order.getIsInvoice(), InvoiceStatusEnum.INVOICE_FAILED.getValue()), ORDER_INVOICED.getCode(), "订单{}已开票", orderNo);
            if(ObjectUtil.equals(order.getIsChangeOrder(),ORDER_CHANGE_TYPE_OLD)){
                //如果是改签原订单，只计算退款手续费
                BigDecimal totalFee = tradeRefundRepositoryService.getOrderRefundFee(order.getId());
                if (totalFee != null) {
                    amt = amt.add(totalFee);
                }
            }else{
                amt = amt.add(order.getPayAmount());
                //减去退款金额
                BigDecimal orderRefundAmount = tradeRefundRepositoryService.getOrderRefundAmount(order.getId());
                if (orderRefundAmount != null) {
                    amt = amt.subtract(orderRefundAmount);
                }
                //减去未使用金额
                BigDecimal unusedAmount = tradeOrderRepositoryService.getUnUsedAmount(order.getId());
                if (unusedAmount != null) {
                    amt = amt.subtract(unusedAmount);
                }
            }
            orderList.add(order);
            if (StringUtils.isBlank(reqDTO.getAccountId()) && Objects.equals(order.getSellMethod(), SellMethodEnum.MINI_APP.getType())) {
                reqDTO.setAccountId(order.getMemberId());
            }
        }
        if (reqDTO.getTotalPrice() == null) {
            reqDTO.setTotalPrice(reqDTO.getPrice());
        }
        boolean isSingInvoice = false;
        //计算开票金额，如果未传值则用订单总金额
        if (reqDTO.getPrice() == null) {
            reqDTO.setPrice(amt);
        } else {
            isSingInvoice = true;
            Asserts.isTrue(amt.compareTo(reqDTO.getTotalPrice()) == 0, "开票金额发生变更，请重新刷新后发起，{},{}", reqDTO.getPrice(), amt);
        }

        InvoiceDO invoiceDO = this.buildInvoice(reqDTO, reqDTO.getPrice());
        invoiceDetailService.createInvoiceDetail(orderList, invoiceDO.getId());

        tradeOrderRepositoryService.updateInvoiceStatus(orderList, InvoiceStatusEnum.INVOICE_INVOIING.getValue());
        String fpSupplier = configApi.getPlatformConfigValueByKey("supplier");
        CommonResult<InvoiceDO> result;
        invoiceDO.setFpSupplier(fpSupplier);
        if (StringUtils.equals("group", fpSupplier)) {
            result = invoiceGroupService.invoiceIssuance(invoiceDO,reqDTO.getRemark());
        } else {
            result = invoiceHangxinService.invoiceIssuance(invoiceDO);
        }
        if (result.isSuccess()) {
            tradeOrderRepositoryService.updateInvoiceStatus(orderList, InvoiceStatusEnum.INVOICE_SUCCESS.getValue());
        } else {
            tradeOrderRepositoryService.updateInvoiceStatus(orderList, InvoiceStatusEnum.INVOICE_FAILED.getValue());
        }


        if(isSingInvoice && reqDTO.getTotalPrice().subtract(reqDTO.getPrice()).compareTo(BigDecimal.ZERO)>0){
            InvoiceDO invoiceDO2 = this.buildInvoice(reqDTO, reqDTO.getTotalPrice().subtract(reqDTO.getPrice()));
            invoiceDetailService.createInvoiceDetail(orderList, invoiceDO2.getId());

            tradeOrderRepositoryService.updateInvoiceStatus(orderList, InvoiceStatusEnum.INVOICE_INVOIING.getValue());
            CommonResult<InvoiceDO> result2;
            invoiceDO2.setFpSupplier(fpSupplier);
            if (StringUtils.equals("group", fpSupplier)) {
                result2 = invoiceGroupService.invoiceIssuance(invoiceDO2,reqDTO.getRemark());
            } else {
                result2 = invoiceHangxinService.invoiceIssuance(invoiceDO2);
            }
            if (result2.isSuccess()) {
                tradeOrderRepositoryService.updateInvoiceStatus(orderList, InvoiceStatusEnum.INVOICE_SUCCESS.getValue());
            }
//            else {
                //第二张发票失败了不处理，可能另外一个发票已经成功
                //tradeOrderRepositoryService.updateInvoiceStatus(orderList, InvoiceStatusEnum.INVOICE_FAILED.getValue());
//            }
        }

        return result;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<InvoiceDO> applyReInvoicing(InvoicingReqDTO reqDTO) {
        List<TradeOrderDO> orderList = new ArrayList<>();
        InvoiceDO invoiceDO = this.rebuildInvoice(reqDTO, reqDTO.getPrice());
        ExecutorService executor = ThreadUtil.newSingleExecutor();
//        CompletableFuture<CommonResult<InvoiceDO>> future = CompletableFuture.supplyAsync(() -> invoiceHangxinService.invoiceIssuance(invoiceDO), executor);
        String fpSupplier = configApi.getPlatformConfigValueByKey("invoice.supplier");
        CompletableFuture<CommonResult<InvoiceDO>> future = CompletableFuture.supplyAsync(() -> {
            if (StringUtils.equals("group", fpSupplier)) {
                return invoiceGroupService.invoiceIssuance(invoiceDO,reqDTO.getRemark());
            } else {
                return invoiceHangxinService.invoiceIssuance(invoiceDO);
            }
        }, executor);
        future.thenAcceptAsync(result -> {
            if (result.isSuccess()) {
                InvoiceDO invoiceRes = result.getData();
                tradeOrderRepositoryService.updateInvoiceStatus(orderList, invoiceRes.getInvoiceStatus());
            }
        }, executor);


        return future.join();

    }

    @Override
    public String queryInvoiceUrl(InvoiceDO invoice) {
        if (invoice == null) {
            throw exception(INVOICE_NOT_EXISTS);
        }
        Asserts.isTrue(invoice.getInvoiceStatus().equals(InvoiceStatusEnum.INVOICE_SUCCESS.getValue()), "发票未开票成功，无法查询！");
        String kprq = DateUtil.format(invoice.getCKprq(), DatePattern.PURE_DATETIME_PATTERN);
        JSONObject resObj = invoiceHangxinService.queryInvoiceUrl(invoice.getId(), invoice.getCFphm(), kprq);
        String resultUrl = "";
        if (null != resObj) {
            resultUrl = resObj.getStr("DATA");
            List<InvoiceDetailDO> list = invoiceDetailService.getInvoiceDetails(invoice.getId());
            List<String> orderNoList = list.stream().map(InvoiceDetailDO::getOrderNo).collect(Collectors.toList());
            //TODO:
//            tradeOrderRepositoryService.updateInvoiceStatusByOrderNo(orderNoList, 1);
            invoice.setCUrl(resultUrl);
            invoiceMapper.updateById(invoice);
        }
        return resultUrl;
    }

    @Override
    public String queryGroupInvoiceUrl(InvoiceDO invoice) {
        if (invoice == null) {
            throw exception(INVOICE_NOT_EXISTS);
        }
        TenantContextHolder.setIgnore(true);
        invoiceGroupService.queryInvoice(invoice);
        TenantContextHolder.setIgnore(false);
        return "";
    }

    private void validateInvoiceExists(Long id) {
        if (invoiceMapper.selectById(id) == null) {
            throw exception(INVOICE_NOT_EXISTS);
        }
    }


    @Override
    public InvoiceDO getInvoice(Long id) {
        return invoiceMapper.selectById(id);
    }

    @Override
    public List<InvoiceDO> queryInvoiceOrder(String orderNo) {
        return invoiceMapper.queryInvoiceOrder(orderNo);
    }

    @Override
    public List<InvoiceDO> getInvoices() {
        return invoiceMapper.selectList(new LambdaQueryWrapperX<InvoiceDO>()
                .eq(InvoiceDO::getInvoiceStatus, 4)
                .isNull(InvoiceDO::getCUrl)
                .gt(InvoiceDO::getCreateTime, DateUtil.yesterday()));
    }

    @Override
    public PageResult<InvoiceRespVO> getInvoicePage(InvoicePageReqVO pageReqVO) {
        if (ThreadLocalUtil.getAgencyId() != null) {
            pageReqVO.setAgencyId(ThreadLocalUtil.getAgencyId().toString());
        }
        Long storeId = agencyAuthUtils.getLoginStoreId();
        if (storeId != null) {
            pageReqVO.setStoreId(storeId);
        }
        IPage<InvoiceRespVO> iPage = MyBatisUtils.buildPage(pageReqVO);
        IPage<InvoiceRespVO> page = invoiceMapper.queryInvoicePage(iPage, pageReqVO);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public void updateInvoice(InvoiceDO invoiceDO) {
        this.invoiceMapper.updateById(invoiceDO);
    }

    private InvoiceDO buildInvoice(InvoicingReqDTO reqDTO, BigDecimal price) {
        InvoiceDO invoiceDO = BeanUtils.toBean(reqDTO, InvoiceDO.class);
        String taxCode = configApi.getPlatformConfigValueByKey("invoice.tax");
        String serialNum = RandomUtil.randomNumbers(30 - taxCode.length());
        long id = IdWorker.getId(invoiceDO);
        invoiceDO.setId(id);
        invoiceDO.setPrice(price);
        if(reqDTO.getSource()!=null && reqDTO.getSource() == 1){
            invoiceDO.setTitleType(0);
        }else{
            if(reqDTO.getTitleType()==null) {
                invoiceDO.setTitleType(1);
            }
        }
        invoiceDO.setInvoiceStatus(InvoiceStatusEnum.INVOICE_FAILED.getValue());
        invoiceDO.setTransactionNo(tradeUUCodeUtils.generateOrderNo(INVOICE_PREFIX));
        invoiceDO.setSalesTaxCode(taxCode);
        invoiceDO.setFpqqlsh(serialNum);
        invoiceDO.setSalesName(configApi.getPlatformConfigValueByKey("invoice.sales.name"));
        invoiceDO.setSalesAddress(configApi.getPlatformConfigValueByKey("invoice.sales.addr"));
        invoiceDO.setDeleted(false);
        if (StringUtils.isNotBlank(reqDTO.getAccountId())) {
            invoiceDO.setAccountId(reqDTO.getAccountId());
        } else {
            invoiceDO.setStoreId(agencyAuthUtils.getLoginStoreId());
        }
        invoiceMapper.insert(invoiceDO);
        return invoiceDO;
    }

    private InvoiceDO rebuildInvoice(InvoicingReqDTO reqDTO, BigDecimal price) {
        InvoiceDO invoiceDO = BeanUtils.toBean(reqDTO, InvoiceDO.class);
        String taxCode = configApi.getPlatformConfigValueByKey("invoice.tax");
        String serialNum = RandomUtil.randomNumbers(30 - taxCode.length());
        long id = IdWorker.getId(invoiceDO);
        if (invoiceDO.getId() == null) {
            invoiceDO.setId(id);
        }
        invoiceDO.setPrice(price);
        invoiceDO.setInvoiceStatus(InvoiceStatusEnum.INVOICE_FAILED.getValue());
        invoiceDO.setTransactionNo(tradeUUCodeUtils.generateOrderNo(INVOICE_PREFIX));
        invoiceDO.setSalesTaxCode(taxCode);
        invoiceDO.setFpqqlsh(serialNum);
        invoiceDO.setSalesName(configApi.getPlatformConfigValueByKey("invoice.sales.name"));
        invoiceDO.setSalesAddress(configApi.getPlatformConfigValueByKey("invoice.sales.addr"));
        invoiceDO.setDeleted(false);
        invoiceMapper.insertOrUpdate(invoiceDO);
        return invoiceDO;
    }


    @Override
    public PageResult<InvoiceDO> getAppInvoicePage(InvoicePageReqVO pageReqVO) {
        return invoiceMapper.selectPage(pageReqVO);
    }

    @Override
    public InvoiceDO queryInvoice(Long id) {
        Long tenantId = Long.parseLong(configApi.getConfigValueByKey("system.tenantId"));
        Long loginTenantId = TenantContextHolder.getTenantId();
        InvoiceDO invoiceDO = null;
        if (tenantId.equals(loginTenantId)) {
            //平台租户忽略租户查询
            TenantContextHolder.setIgnore(true);
            invoiceDO = getInvoice(id);
            if (invoiceDO.getInvoiceStatus() == 4) {
                invoiceDO = invoiceGroupService.queryInvoice(invoiceDO).getCheckedData();
            }
            if (invoiceDO.getInvoiceStatus() == 5) {
                invoiceDO = invoiceGroupService.queryRedInvoice(invoiceDO).getCheckedData();
            }
            TenantContextHolder.setIgnore(false);
        }
        return invoiceDO;
    }

    @Override
    public InvoiceDO redInvoice(Long id) {
        Long tenantId = Long.parseLong(configApi.getConfigValueByKey("system.tenantId"));
        Long loginTenantId = TenantContextHolder.getTenantId();
        InvoiceDO invoiceDO = null;
        if (tenantId.equals(loginTenantId)) {
            //平台租户忽略租户查询
            TenantContextHolder.setIgnore(true);
            invoiceDO = getInvoice(id);
            invoiceGroupService.redInvoiceIssuance(invoiceDO);
            TenantContextHolder.setIgnore(false);
        }
        return invoiceDO;
    }
}
