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

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 com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yc.ship.framework.common.exception.ServiceException;
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.security.core.LoginUser;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.ota.api.OtaDistributorApi;
import com.yc.ship.module.ota.api.dto.DistributorRespDTO;
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.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.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 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.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.yc.ship.module.otc.enums.ErrorCodeConstants.NOT_ASSOCIATED_DISTRIBUTOR;
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 OtaDistributorApi otaDistributorApi;

    //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());
            }
        }
        BigDecimal zk = BigDecimal.ZERO;
        //查询订单及金额
        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);
            amt = amt.add(order.getPayAmount());
            zk=zk.add(order.getAmount().subtract(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());
        }
        reqDTO.setZkprice(zk);
        //计算开票金额，如果未传值则用订单总金额
        if (reqDTO.getPrice() == null) {
            reqDTO.setPrice(amt);
        } else {
            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");
        invoiceDO.setFpSupplier(fpSupplier);
        CommonResult<InvoiceDO> result = invoiceGroupService.invoiceIssuance(invoiceDO,reqDTO.getRemark());
        if (result.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(() -> {
                return invoiceGroupService.invoiceIssuance(invoiceDO,reqDTO.getRemark());

        }, executor);
        future.thenAcceptAsync(result -> {
            if (result.isSuccess()) {
                InvoiceDO invoiceRes = result.getData();
                tradeOrderRepositoryService.updateInvoiceStatus(orderList, invoiceRes.getInvoiceStatus());
            }
        }, executor);


        return future.join();

    }

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


    @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) {
        LoginUser agencyLoginUser = agencyAuthUtils.getAgencyLoginUser();
        if (agencyLoginUser.getDistributorId() != null) {
            pageReqVO.setAgencyId(agencyLoginUser.getDistributorId().toString());
        }
        if (agencyLoginUser.getStoreId() != null) {
            pageReqVO.setStoreId(agencyLoginUser.getStoreId());
        }

        IPage<InvoiceRespVO> iPage = MyBatisUtils.buildPage(pageReqVO);
        IPage<InvoiceRespVO> page = invoiceMapper.queryInvoicePage(iPage, pageReqVO);
        String downurl = configApi.getPlatformConfigValueByKey("newinvoice.downurl");
        page.getRecords().forEach(invoice -> {
            if(StringUtils.isNotBlank(invoice.getCUrl())) {
                invoice.setCUrl(downurl + invoice.getCUrl());
            }
            if(StringUtils.isBlank(invoice.getFpSupplier())) {
                invoice.setFpSupplier("group");
            }
        });
        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("newinvoice.group.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.setZkprice(reqDTO.getZkprice());
        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;
    }
}
