package com.trade.service.trade.filingdoc.service.impl;

import java.io.IOException;
import java.util.*;

import javax.annotation.Resource;

import cn.hutool.core.date.LocalDateTimeUtil;
import com.trade.service.enterprise.customer.dto.EnterpriseXtIdsDto;
import com.trade.service.trade.filingdoc.service.FilingDocService;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.trade.common.utils.ConstantUtil;
import com.trade.common.utils.DateUtils;
import com.trade.common.utils.StringUtils;
import com.trade.service.TransStruct;
import com.trade.service.enterprise.companyrelated.domain.VendorEntity;
import com.trade.service.enterprise.companyrelated.service.IVendorEntityService;
import com.trade.service.enterprise.customer.domain.Enterprise;
import com.trade.service.enterprise.customer.service.EnterpriseBaseService;
import com.trade.service.filestorage.entity.BusinessRelateFile;
import com.trade.service.filestorage.entity.TFileStorage;
import com.trade.service.filestorage.enums.FileTypeEnum;
import com.trade.service.filestorage.service.BusinessRelateFileBaseService;
import com.trade.service.filestorage.service.TFileStorageService;
import com.trade.service.filestorage.util.FileUtil;
import com.trade.service.filestorage.util.oss.OSSUtil;
import com.trade.service.finance.constant.BillingConstant;
import com.trade.service.finance.paymentcollection.domain.BillingIncome;
import com.trade.service.finance.paymentcollection.service.BillingIncomeService;
import com.trade.service.thirdparty.xiaotu.configuration.XiaotuConfiguration;
import com.trade.service.thirdparty.xiaotu.dto.BusinessCommonRequest;
import com.trade.service.thirdparty.xiaotu.dto.filingdoc.*;
import com.trade.service.thirdparty.xiaotu.service.XiaotuRequestService;
import com.trade.service.trade.filingdoc.domain.FilingDocWriteTask;
import com.trade.service.trade.filingdoc.service.IFilingDocWriteTaskService;
import com.trade.service.trade.purchasecontract.dto.PurchaseContractDetailDto;
import com.trade.service.trade.purchasecontract.service.PurchaseContractBaseService;
import com.trade.service.trade.purchaseorder.domain.PurchaseOrder;
import com.trade.service.trade.purchaseorder.service.PurchaseOrderLogisticsService;
import com.trade.service.trade.purchaseorder.service.PurchaseOrderService;
import com.trade.service.trade.purchaseorder.vo.PurchaseOrderLogisticsVo;
import com.trade.service.trade.salescontract.domain.ContractFile;
import com.trade.service.trade.salescontract.dto.SalesContractDto;
import com.trade.service.trade.salescontract.service.ContractFileService;
import com.trade.service.trade.salescontract.service.SalesContractService;
import com.trade.service.trade.salesorder.domain.SalesOrder;
import com.trade.service.trade.salesorder.domain.SalesOrderLogistics;
import com.trade.service.trade.salesorder.service.ISalesOrderGoodsService;
import com.trade.service.trade.salesorder.service.ISalesOrderLogisticsService;
import com.trade.service.trade.salesorder.service.ISalesOrderService;
import com.trade.service.trade.salesorder.vo.SalesOrderGoodsVO;
import com.trade.service.tradingplatform.domain.TradingPlatform;
import com.trade.service.tradingplatform.service.TradingPlatformBaseService;

import cn.hutool.core.collection.CollectionUtil;
import liquibase.util.MD5Util;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class FilingDocServiceImpl implements FilingDocService {

    @Resource
    private IFilingDocWriteTaskService filingDocWriteTaskService;
    @Resource
    private ISalesOrderService saleOrderService;
    @Resource
    private EnterpriseBaseService enterpriseBaseService;
    @Resource
    private XiaotuRequestService xiaotuRequestService;
    @Resource
    private XiaotuConfiguration xiaotuConfiguration;
    @Resource
    private SalesContractService salesContractService;
    @Resource
    private TradingPlatformBaseService tradingPlatformBaseService;
    @Resource
    private OSSUtil ossUtil;
    @Resource
    private TFileStorageService tFileStorageService;
    @Resource
    private ContractFileService contractFileService;
    @Resource
    private ISalesOrderGoodsService salesOrderGoodsService;
    @Resource
    private TransStruct transStruct;
    @Resource
    private PurchaseContractBaseService purchaseContractBaseService;
    @Resource
    private PurchaseOrderService purchaseOrderService;
    @Resource
    private ISalesOrderLogisticsService salesOrderLogisticsService;
    @Resource
    private PurchaseOrderLogisticsService purchaseOrderLogisticsService;
    @Resource
    private BusinessRelateFileBaseService businessRelateFileBaseService;
    @Resource
    private IVendorEntityService vendorEntityService;
    @Resource
    private BillingIncomeService billingIncomeService;

    @Override
    @Async
    public void newTaskBySalesOrderId(Long salesOrderId, Integer part) {
        newTask(saleOrderService.getById(salesOrderId), part);
    }

    @Override
    @Async
    public void newTaskBySalesOrderNo(String saleOrderNo, Integer part) {
        newTask(saleOrderService.getByOrderNo(saleOrderNo), part);
    }

    private void newTask(SalesOrder salesOrder, Integer part) {
        SalesContractDto contract = salesContractService.getSalesContractById(salesOrder.getSaleContractId());
        String certNo = contract.getAgencyCertNo();
        boolean agentInd = true;
        if (StringUtils.isEmpty(contract.getAgencyCertNo())) {// 非代理模式取客户
            Enterprise enterprise = enterpriseBaseService.getEnterpriseByCustomerId(salesOrder.getCustomerUserId());
            certNo = enterprise.getCertNo();
            agentInd = false;
        }
        FilingDocWriteTask task = new FilingDocWriteTask(salesOrder.getOrderNo(), certNo, agentInd, part);
        boolean isSuccess = false;
        try {
            isSuccess = sendWrite(task, part);
        } catch (Exception e) {
            log.error("Failed to write filing document for sales order: " + salesOrder.getId(), e);
            e.printStackTrace();
        }
        task.setStatus(isSuccess ? 1 : 0);
        filingDocWriteTaskService.save(task);
    }

    /**
     * 发送写入请求
     * 
     * @param task 任务参数
     * @param part 写入阶段
     * @return 写入是否成功
     */
    private boolean sendWrite(FilingDocWriteTask task, int part) {
        FilingDocWriteRequest request = initFilingDocWriteRequest(task, part);
        if (CollectionUtils.isEmpty(request.getBadzList())) {
            return false;
        }
        BusinessCommonRequest businessCommonRequest = getBusinessCommonRequest4FilingDoc(task);
        return xiaotuRequestService.filingDocumentWrite(request, businessCommonRequest);
    }

    /**
     * 获取小图业务请求参数 如果是代理模式，则token 用户取贸易平台，否则取客户信息
     * 
     * @param task 任务参数
     * @return 业务请求参数
     */
    private BusinessCommonRequest getBusinessCommonRequest4FilingDoc(FilingDocWriteTask task) {
        BusinessCommonRequest businessCommonRequest = new BusinessCommonRequest();
        if (task.getAgentInd()) {
            TradingPlatform tradingPlatform = tradingPlatformBaseService.findByCertNo(task.getCertNo());
            businessCommonRequest.setXtEid(tradingPlatform.getXtEid());
            businessCommonRequest.setXtUid(tradingPlatform.getXtUserId());
        } else {
            EnterpriseXtIdsDto xtIdsDto = enterpriseBaseService.getEnterpriseXtIdsDto(task.getCertNo());
            businessCommonRequest.setXtEid(xtIdsDto.getXtEid());
            businessCommonRequest.setXtUid(xtIdsDto.getXtUid());
        }
        return businessCommonRequest;
    }

    private FilingDocWriteRequest initFilingDocWriteRequest(FilingDocWriteTask task, int part) {
        FilingDocWriteRequest request = new FilingDocWriteRequest();
        request.setShxydm(task.getCertNo());
        long timestamp = System.currentTimeMillis();
        request.setKey(generateKey(task.getCertNo(), timestamp, xiaotuConfiguration.getWriteKey()));
        request.setTime(timestamp);
        request.setBscjtsUuid(UUID.randomUUID().toString().replaceAll("-", ""));
        request.setBadzList(getFilingDocWriteItems(task, part));
        return request;
    }

    private List<FilingDocWriteItem> getFilingDocWriteItems(FilingDocWriteTask task, int part) {
        SalesOrder salesOrder = saleOrderService.getByOrderNo(task.getBizNo());
        String sellerName;
        String sellerCertNo;
        if (task.getAgentInd()) {
            TradingPlatform tradingPlatform = tradingPlatformBaseService.findByCertNo(task.getCertNo());
            sellerName = tradingPlatform.getPlatformName();
            sellerCertNo = tradingPlatform.getCertNo();
        } else {
            Enterprise enterprise = enterpriseBaseService.getEnterpriseByCertNo(task.getCertNo());
            sellerName = enterprise.getName();
            sellerCertNo = enterprise.getCertNo();
        }
        List<SalesOrderGoodsVO> salesOrderGoodsList = salesOrderGoodsService.listByOrderId(salesOrder.getId());
        List<FilingDocWriteItemDetailGoods> salesGoodsList = transStruct
                .transSalesOrderGoodsList4FilingDoc(salesOrderGoodsList);
        SalesContractDto contract = salesContractService.getSalesContractById(salesOrder.getSaleContractId());
        ContractFile salesContractFile = contractFileService.lambdaQuery()
                .eq(ContractFile::getContractNo, contract.getContractNo()).eq(ContractFile::getDeleted, 0).one();
        FilingDocWriteItem item = new FilingDocWriteItem();
        item.setYwbh(task.getBizNo());
        item.setCkxsht(
                getFilingDocWriteItemCKXSHT(sellerName, task.getBizNo(), salesGoodsList, contract, salesContractFile));
        // 一个采购合同对应多个采购订单
        List<PurchaseOrder> purchaseOrderList = purchaseOrderService.listBySaleOrderId(salesOrder.getId());
        // 采购订单不一定会有：自营出口、生产型企业 无采购相关信息
        if (CollectionUtil.isNotEmpty(purchaseOrderList)) {
            item.setGhht(getFilingDocWriteItemGHHT(sellerName, purchaseOrderList));
        }
        SalesOrderLogistics orderLogistics = salesOrderLogisticsService.getByOrderId(salesOrder.getId());
        if (orderLogistics != null) {
            item.setTyd(getFilingDocWriteItemTYD(orderLogistics, salesGoodsList));
        }
        // 运输发票
        item.setYsfp(getFilingDocWriteItemYSFP(sellerName, sellerCertNo, purchaseOrderList));
        // 这里本来设计为两个阶段分别传输不同单证信息，后根据小图反馈，每次写入全量单据
        if (part == 2) {
            item.setQtdz(secondPartFilingDocWriteItems(task));
        }
        return Collections.singletonList(item);
    }

    private List<FilingDocWriteItemCKXSHT> getFilingDocWriteItemCKXSHT(String sellerName, String bizNo,
            List<FilingDocWriteItemDetailGoods> detailGoodsList, SalesContractDto contract,
            ContractFile salesContractFile) {
        FilingDocWriteItemCKXSHT ckxsht = new FilingDocWriteItemCKXSHT();
        Pair<String, String> fileInfo = getFileInfo4Write(salesContractFile.getSignedFileStorageId());
        ckxsht.setWjm(fileInfo.getLeft());
        ckxsht.setWjnr(fileInfo.getRight());
        // 根据20241024确认，此处合同编号取值为销售订单编号
        ckxsht.setHtbh(bizNo);
        ckxsht.setHtje(String.valueOf(contract.getContractAmount()));
        ckxsht.setJfmc(contract.getImporterEnterpriseName());
        ckxsht.setYfmc(sellerName);
        ckxsht.setQdrq(contract.getSignDate().toString());
        ckxsht.setSpxxArray(detailGoodsList);
        return Collections.singletonList(ckxsht);
    }

    private List<FilingDocWriteItemGHHT> getFilingDocWriteItemGHHT(String sellerName,
            List<PurchaseOrder> purchaseOrderList) {
        List<FilingDocWriteItemGHHT> ghhtList = new ArrayList<>();
        for (PurchaseOrder purchaseOrder : purchaseOrderList) {
            // 根据用户ID和采购合同编号查询采购合同详情
            PurchaseContractDetailDto purchaseContractDetailDto = purchaseContractBaseService
                    .findByCustomerUserIdAndPurchaseContractNo(purchaseOrder.getCustomerUserId(),
                            purchaseOrder.getPurchaseContractNo());
            if (purchaseContractDetailDto == null) {
                continue;
            }
            FilingDocWriteItemGHHT ghht = new FilingDocWriteItemGHHT();
            Pair<String, String> fileInfo = getFileInfo4Write(
                    purchaseContractDetailDto.getPurchaseContractFile().getId());
            ghht.setWjm(fileInfo.getLeft());
            ghht.setWjnr(fileInfo.getRight());
            if (StringUtils.isNotEmpty(purchaseContractDetailDto.getVendorEntityName())) {
                ghht.setYfmc(purchaseContractDetailDto.getVendorEntityName());
            }
            // 取采购订单编号
            ghht.setHtbh(purchaseOrder.getOrderNo());
            // 取采购订单金额
            ghht.setHtje(String.valueOf(purchaseOrder.getAmountTotal()));
            ghht.setJfmc(sellerName);
            ghht.setQdrq(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, purchaseContractDetailDto.getSignDate()));
            ghht.setSpxxArray(
                    transStruct.transPurchaseOrderDetailList4FilingDoc(purchaseContractDetailDto.getCommodityList()));
            ghhtList.add(ghht);
        }
        return ghhtList;
    }

    private List<FilingDocWriteItemTYD> getFilingDocWriteItemTYD(SalesOrderLogistics orderLogistics,
            List<FilingDocWriteItemDetailGoods> detailGoodsList) {
        FilingDocWriteItemTYD tyd = new FilingDocWriteItemTYD();
        tyd.setTydlxDm(orderLogistics.getLadingType());
        tyd.setTydhm(orderLogistics.getLadingBillNo());
        tyd.setMdd(orderLogistics.getDestPort());
        Pair<String, String> fileInfo = getFileInfo4Write(orderLogistics.getLadingBillFileId());
        tyd.setWjm(fileInfo.getLeft());
        tyd.setWjnr(fileInfo.getRight());
        tyd.setSpxxArray(detailGoodsList);
        return Collections.singletonList(tyd);
    }

    private List<FilingDocWriteItemYSFP> getFilingDocWriteItemYSFP(String sellerName, String sellerCertNo,
            List<PurchaseOrder> purchaseOrderList) {
        List<FilingDocWriteItemYSFP> xsfpList = new ArrayList<>();
        for (PurchaseOrder purchaseOrder : purchaseOrderList) {
            // 根据用户ID和采购合同编号查询采购合同详情
            Optional<PurchaseContractDetailDto> purchaseContractDetailDtoOpt = Optional
                    .ofNullable(purchaseContractBaseService.findByCustomerUserIdAndPurchaseContractNo(
                            purchaseOrder.getCustomerUserId(), purchaseOrder.getPurchaseContractNo()));
            PurchaseOrderLogisticsVo orderLogistics = purchaseOrderLogisticsService
                    .queryByPurchaseOrderId(purchaseOrder.getId());
            BusinessRelateFile relateFile = businessRelateFileBaseService.getByCondition(orderLogistics.getId(),
                    ConstantUtil.PURCHASE_ORDER, FileTypeEnum.PURCHASE_ORDER_TRANS_PROVE.getCode());
            FilingDocWriteItemYSFP xsfp = new FilingDocWriteItemYSFP();
            Pair<String, String> fileInfo = getFileInfo4Write(relateFile.getFileId());
            xsfp.setFplxDm("01");// 出口企业承付运费的国内运输发票
            xsfp.setFphm(orderLogistics.getLogisticsOrderNumber());
            xsfp.setWjm(fileInfo.getLeft());
            xsfp.setWjnr(fileInfo.getRight());
            xsfp.setKprq(LocalDateTimeUtil.format(orderLogistics.getKprq(), DateUtils.YYYY_MM_DD));
            xsfp.setGhfmc(sellerName);
            xsfp.setGfnsrsbh(sellerCertNo);
            purchaseContractDetailDtoOpt.ifPresent(purchaseContractDetailDto -> {
                if (StringUtils.isNotEmpty(purchaseContractDetailDto.getVendorEntityName())) {
                    xsfp.setXhfmc(purchaseContractDetailDto.getVendorEntityName());
                    VendorEntity vendorEntity = vendorEntityService
                            .getByEntityNo(purchaseContractDetailDto.getVendorEntityNo());
                    xsfp.setXfnsrsbh(vendorEntity.getCertNo());
                }
            });
            xsfpList.add(xsfp);
        }
        return xsfpList;
    }

    private List<FilingDocWriteItemQTDZ> secondPartFilingDocWriteItems(FilingDocWriteTask task) {
        List<BillingIncome> incomeList = billingIncomeService.lambdaQuery()
                .eq(BillingIncome::getSaleOrderNo, task.getBizNo()).list();
        if (CollectionUtils.isEmpty(incomeList)) {
            return Collections.emptyList();
        }
        List<FilingDocWriteItemQTDZ> qtdzList = new ArrayList<>();
        for (BillingIncome income : incomeList) {
            FilingDocWriteItemQTDZ qtdz = new FilingDocWriteItemQTDZ();
            BusinessRelateFile relateFile = businessRelateFileBaseService.getByCondition(income.getId(),
                    BillingConstant.ERECEIVE_PAYMENT_VOUCHER, FileTypeEnum.DISBURSEMENT_ENTRY.getCode());
            Pair<String, String> fileInfo = getFileInfo4Write(relateFile.getFileId());
            qtdz.setWjm(fileInfo.getLeft());
            qtdz.setWjnr(fileInfo.getRight());
            qtdz.setDzlx("08");
        }
        return qtdzList;
    }

    private Pair<String, String> getFileInfo4Write(Long fileStorageId) {
        TFileStorage fileStorage = tFileStorageService.getFileStorageById(fileStorageId);
        byte[] bytes;
        try {
            bytes = FileUtil.toByteArray(ossUtil.downloadFile(fileStorage.getPath()));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to download file from oss.");
        }
        return ImmutablePair.of(fileStorage.getOriginalName(), new String(Base64.encodeBase64(bytes)));
    }

    /**
     * 生成请求参数的key
     * 
     * @param certNo 统一社会信用代码
     * @param timestamp 时间戳
     * @param writeKey 写入密钥
     * @return 请求参数的key
     */
    private String generateKey(String certNo, long timestamp, String writeKey) {
        String key = certNo + timestamp + writeKey;
        return MD5Util.computeMD5(key);
    }

    /**
     * 小图定时写入任务
     */
    public void writeFilingDoc() {
        log.info("Writing filing documents to xiaotu...");
        List<FilingDocWriteTask> tasks = filingDocWriteTaskService.findTaskByStatus(0);
        for (FilingDocWriteTask task : tasks) {
            if (sendWrite(task, task.getPart())) {
                task.setStatus(1);
                filingDocWriteTaskService.updateTask(task);
            }
        }
        log.info("Writing filing documents to xiaotu finished.");
    }
}
