package com.jwipc.finance.logic.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.jwipc.api.constant.RestCode;
import com.jwipc.api.dto.SapResposeBody;
import com.jwipc.exception.DaoException;
import com.jwipc.finance.constant.AccountStatementSapClient;
import com.jwipc.finance.convert.InvoicedApplyDetailConvert;
import com.jwipc.finance.dto.AccountStatementToSapDTO;
import com.jwipc.finance.dto.InvoicedApplyItemDTO;
import com.jwipc.finance.entity.*;
import com.jwipc.finance.logic.AccountStatementLogic;
import com.jwipc.finance.logic.InvoicedApplyDetailLogic;
import com.jwipc.finance.model.po.InvoicedApplyItemPO;
import com.jwipc.finance.model.query.CreateInvoicedApplyPageQuery;
import com.jwipc.finance.service.*;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.prop.PriceHandle;
import com.jwipc.security.LoginUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static com.baomidou.dynamic.datasource.support.DdConstants.SLAVE;

/**
 * @author shenyang.ou
 * @date 2024/3/5 12:57
 * @desc
 */
@Service
@RequiredArgsConstructor
public class InvoicedApplyDetailLogicImpl implements InvoicedApplyDetailLogic {
    private final IInvoicedApplyService invoicedApplyService;
    private final IInvoicedApplyDetailService invoicedApplyDetailService;
    private final AccountStatementLogic accountStatementLogic;
    private final AccountStatementSapClient accountStatementSapClient;
    private final IAccountStatementService accountStatementService;
    private final IInvoicedApplyPostResultService invoicedApplyPostResultService;
    private final IAccountStatementDetailService accountStatementDetailService;
    private final InvoicedApplyDetailConvert invoicedApplyDetailConvert;
    private final PriceHandle priceHandle;
    /**
     * 获取已经存在开票申请中的对账单明细ID
     *
     * @param supplierCode 供应商编码
     * @param companyCode  公司编码
     * @return 结果
     */
    @Override
    public List<Long> findExistAsdIdByQuery(String supplierCode, String companyCode) {
        if(StringUtils.isEmpty(supplierCode)){
            return Lists.newArrayList();
        }
        if(StringUtils.isEmpty(companyCode)){
            return Lists.newArrayList();
        }
        return invoicedApplyDetailService.findExistAsdIdByQuery(supplierCode, companyCode);
    }

    /**
     * 提供采购确认时获取开票申请明细数据
     *
     * @param iaId     开票申请单ID
     * @param becShare 后端计算分摊 0:否 1:是
     * @param head     开票申请主表数据
     * @return 结果
     */
    @Override
    public List<InvoicedApplyItemDTO> providePurchaseConfirm(Long iaId, Integer becShare, InvoicedApply head) {
        List<InvoicedApplyItemPO> invoicedApplyItemPOS = invoicedApplyDetailService.providePurchaseConfirm(iaId);
        List<InvoicedApplyItemDTO> list = invoicedApplyDetailConvert.po2DtoList(invoicedApplyItemPOS);
        // 后台计算一次分摊, 主要是防止页面加载缓慢的问题
        if(1 == becShare){
            this.calculationShare(list, head);
        }
        return list;
    }

    /**
     * 获取开票申请单明细数据根据开票申请单ID
     *
     * @param iaId     开票申请单ID
     * @param becShare 后端计算分摊 0:否 1:是
     * @return 结果
     */
    @Override
    public PageView findByApplyId(Long iaId, Integer becShare) {
        InvoicedApply head = null;
        if (1 == becShare) {
            head = invoicedApplyService.getById(iaId);
        }
        List<InvoicedApplyItemDTO> list = this.providePurchaseConfirm(iaId, becShare, head);
        Page<InvoicedApplyItemDTO> toPage = new Page<>(1, list.size());
        toPage.setRecords(list);
        PageView pageView = PageUtil.getPV(list, list.size());
        BigDecimal notTaxAmount = BigDecimal.ZERO, taxAmount = BigDecimal.ZERO, diffTaxAmount = BigDecimal.ZERO, iaDiffAmount = BigDecimal.ZERO,
                asNotTaxAmount = BigDecimal.ZERO, asTaxAmount = BigDecimal.ZERO, asDiffTaxAmount = BigDecimal.ZERO, asDiffAmount = BigDecimal.ZERO,
                asNtAmount = BigDecimal.ZERO, asItAmount = BigDecimal.ZERO, asDtAmount = BigDecimal.ZERO,
                shareRebateAmount = BigDecimal.ZERO, shareRntAmount = BigDecimal.ZERO, sraTaxAmount = BigDecimal.ZERO,
                shareDeductionAmount = BigDecimal.ZERO, shareDntAmount = BigDecimal.ZERO, sdaTaxAmount = BigDecimal.ZERO,
                shareAfterNta = BigDecimal.ZERO, shareAfterIta = BigDecimal.ZERO, shareAfterTa = BigDecimal.ZERO,
                statementNum = BigDecimal.ZERO;
        for (InvoicedApplyItemDTO dto : list) {
            // 到票相关金额合计
            notTaxAmount = notTaxAmount.add(dto.getNotTaxAmount());
            taxAmount = taxAmount.add(dto.getTaxAmount());
            diffTaxAmount = diffTaxAmount.add(dto.getDiffTaxAmount());
            iaDiffAmount = iaDiffAmount.add(dto.getIaDiffAmount());
            // 对账相关金额合计
            asNotTaxAmount = asNotTaxAmount.add(dto.getAsNotTaxAmount() == null?BigDecimal.ZERO:dto.getAsNotTaxAmount());
            asTaxAmount = asTaxAmount.add(dto.getAsTaxAmount()== null?BigDecimal.ZERO:dto.getAsTaxAmount());
            asDiffTaxAmount = asDiffTaxAmount.add(dto.getAsDiffTaxAmount()== null?BigDecimal.ZERO:dto.getAsDiffTaxAmount());
            asDiffAmount = asDiffAmount.add(dto.getAsDiffAmount()== null?BigDecimal.ZERO:dto.getAsDiffAmount());
            // 订单相关金额合计
            asNtAmount = asNtAmount.add(dto.getAsNtAmount());
            asItAmount = asItAmount.add(dto.getAsItAmount());
            asDtAmount = asDtAmount.add(dto.getAsDtAmount());
            // 分摊返利金额
            shareRebateAmount = shareRebateAmount.add(dto.getShareRebateAmount());
            shareRntAmount = shareRntAmount.add(dto.getShareRntAmount());
            sraTaxAmount = sraTaxAmount.add(dto.getSraTaxAmount());
            // 分摊扣款
            shareDeductionAmount = shareDeductionAmount.add(dto.getShareDeductionAmount());
            shareDntAmount = shareDntAmount.add(dto.getShareDntAmount());
            sdaTaxAmount = sdaTaxAmount.add(dto.getSdaTaxAmount());
            // 分摊后凭证金额
            shareAfterNta = shareAfterNta.add(dto.getShareAfterNta());
            shareAfterIta = shareAfterIta.add(dto.getShareAfterIta());
            shareAfterTa = shareAfterTa.add(dto.getShareAfterTa());

            statementNum = statementNum.add(dto.getStatementNum());
        }

        JSONArray footer = new JSONArray();
        JSONObject obj = new JSONObject();
        obj.put("statementNum", statementNum);
        obj.put("notTaxAmount", notTaxAmount);
        obj.put("taxAmount", taxAmount);
        obj.put("diffTaxAmount", diffTaxAmount);
        obj.put("iaDiffAmount", iaDiffAmount);
        obj.put("asNotTaxAmount", asNotTaxAmount);
        obj.put("asTaxAmount", asTaxAmount);
        obj.put("asDiffTaxAmount", asDiffTaxAmount);
        obj.put("asDiffAmount", asDiffAmount);
        obj.put("asNtAmount", asNtAmount);
        obj.put("asItAmount", asItAmount);
        obj.put("asDtAmount", asDtAmount);
        obj.put("shareRntAmount", shareRntAmount);
        obj.put("shareRebateAmount", shareRebateAmount);
        obj.put("sraTaxAmount", sraTaxAmount);
        obj.put("shareDntAmount", shareDntAmount);
        obj.put("shareDeductionAmount", shareDeductionAmount);
        obj.put("sdaTaxAmount", sdaTaxAmount);
        obj.put("shareAfterNta", shareAfterNta);
        obj.put("shareAfterIta", shareAfterIta);
        obj.put("shareAfterTa", shareAfterTa);
        footer.add(obj);
        pageView.setFooter(footer);
        return pageView;
    }

    /**
     * 更新开票申请单明细行号问题
     *
     * @param iaId 开票申请单ID
     */
    @Override
    public void updateDetailItemNo(Long iaId) {
        final List<InvoicedApplyDetail> list = invoicedApplyDetailService.findByInvoiceApplyId(iaId, Lists.newArrayList());

        List<InvoicedApplyDetail> iadList = Lists.newArrayList();
        int i = 1;
        for(InvoicedApplyDetail iad: list){
            InvoicedApplyDetail updateIad = new InvoicedApplyDetail();
            updateIad.setId(iad.getId());
            updateIad.setItemNo(StringUtils.leftPad(String.valueOf(i*10), 6, "0"));
            iadList.add(updateIad);

            i++;
        }

        invoicedApplyDetailService.updateBatchById(iadList);
    }

    /**
     * 查询贷项凭证数量
     *
     * @param invoiceNo
     * @return
     */
    @Override
    public List<InvoicedApplyDetail> findByInvoiceNo(String invoiceNo) {
        return invoicedApplyDetailService.findByInvoiceNo(invoiceNo);
    }

    /**
     * 创建预制发票
     *
     * @param iaId            开票申请id
     * @param type            1:预制 2:删除
     * @param invoicePostDate 发票过账日期
     * @param statementType   过账类型
     */
    @Override
    public void createPrefabricateInvoice(Long iaId, Integer type, String invoicePostDate, String statementType) {
        InvoicedApply invoicedApply = invoicedApplyService.getById(iaId);
        if (!Objects.equals(invoicedApply.getFinanceConfirmUserId(), LoginUtil.getUserId())){
            throw new DaoException("当前登录人员必须与财务复核人一致，才允许过账");
        }
        //过账状态 1:暂存 2:待预制 3:预制失败 7:已冲销 2024-08-21 邓京术 财务要求 3和7的也允许过账
        if (ObjectUtil.isNotEmpty(invoicedApply)){
            if (invoicedApply.getPostStatus() != 1&&invoicedApply.getPostStatus() != 2&&invoicedApply.getPostStatus() != 3&&invoicedApply.getPostStatus() != 7){
                throw new DaoException(StrUtil.format("开票申请单:{}已过账，不允许再次过账",invoicedApply.getInvoiceApplyNo()));
            }
        }
        InvoicedApplyPostResult invoicedApplyPostResult = invoicedApplyPostResultService.getOne(Wrappers.lambdaQuery(InvoicedApplyPostResult.class).eq(InvoicedApplyPostResult::getInvoiceApplyId,invoicedApply.getId()).last(" limit 1"));
        AccountStatement accountStatement = accountStatementService.getById(invoicedApply.getAccountStatementId());
        List<AccountStatementDetail> asdList = accountStatementDetailService.list(Wrappers.lambdaQuery(AccountStatementDetail.class).eq(AccountStatementDetail::getAsId,invoicedApply.getAccountStatementId()));
        AccountStatementToSapDTO accountStatementToSapDTO = accountStatementLogic.makeAccountStatementSapData(5,"",accountStatement,invoicedApply,asdList,invoicePostDate);
        SapResposeBody sapResposeBody = accountStatementSapClient.syncAccountStatement(accountStatementToSapDTO);
        if (sapResposeBody.getES_RET().getCODE().equals(RestCode.SAP_SUCCESS.getCode())) {
            invoicedApplyPostResult.setSyncMessage("成功");
            invoicedApplyPostResult.setInvoicePostDate(LocalDateTimeUtil.parse(invoicePostDate,"yyyy-MM-dd"));
//            invoicedApplyPostResult.setPostDate(LocalDateTimeUtil.parse(invoicePostDate,"yyyy-MM-dd"));
            invoicedApplyPostResult.setDataStatus(2);
            invoicedApplyPostResult.setOptPostTime(LocalDateTime.now());
            invoicedApplyPostResult.setPostUserName(LoginUtil.getName());
            invoicedApplyPostResult.setPostUserId(LoginUtil.getUserId());
            invoicedApplyPostResult.setPostUserCode(LoginUtil.getAccount());
            invoicedApplyPostResultService.updateById(invoicedApplyPostResult);
            //修改申请单状态
            //发票冲账
//            if (!"A".equals(statementType)){
//                invoicedApply.setPostStatus(5);
//                this.strikePost(iaId);
//            }else {
//                invoicedApply.setPostStatus(4);
//            }
            invoicedApply.setPostStatus(4);
            invoicedApplyService.updateById(invoicedApply);
        }else {
            invoicedApplyPostResult.setSyncMessage(sapResposeBody.getES_RET().getMSG());
//            invoicedApplyPostResult.setPostDate(LocalDateTimeUtil.parse(invoicePostDate,"yyyy-MM-dd"));
            invoicedApplyPostResult.setInvoicePostDate(LocalDateTimeUtil.parse(invoicePostDate));
            invoicedApplyPostResult.setDataStatus(1);
            invoicedApplyPostResult.setOptPostTime(LocalDateTime.now());
            invoicedApplyPostResultService.updateById(invoicedApplyPostResult);
            //修改申请单状态
            invoicedApply.setPostStatus(3);
            invoicedApplyService.updateById(invoicedApply);
            throw new DaoException("SAP接口报错：" + sapResposeBody.getES_RET().getMSG());
        }
    }

    /**
     * 计算扣款或返利的分摊
     * 后台计算一次分摊, 主要是防止页面加载缓慢的问题
     *
     * @param list 待处理的数据
     * @param head 开票申请主表数据
     */
    private void calculationShare(List<InvoicedApplyItemDTO> list, InvoicedApply head){
        // 注意点: 1.分摊标识; 2.对账数量是否小于0; 3.存在冲销时分摊比例问题
        // 入库凭证含税金额; 入库凭证未税金额;
        BigDecimal ita = BigDecimal.ZERO, nta = BigDecimal.ZERO;
        // 表头 -> 返利含税金额; 返利未税金额; 扣款含税金额; 扣款未税金额;
        BigDecimal rta = head.getRebateAmount(), rna = head.getRntAmount(), dta = head.getDeductionAmount(), dna = head.getDntAmount();

        // 开票申请中的税率
        int taxValue = 0;
        // 计算入库凭证合计
        for(InvoicedApplyItemDTO dto: list){
            if(dto.getStatementNum().compareTo(BigDecimal.ZERO) > 0 && 1 == dto.getShareFlag()){
                ita = ita.add(dto.getTaxAmount());
                nta = nta.add(dto.getNotTaxAmount());
            }

            taxValue = dto.getTaxValue();
        }

        // 后续计算的税率 = (0.13 + 1)
        final BigDecimal tax = new BigDecimal(taxValue).divide(new BigDecimal("100"), priceHandle.getPrecision(), priceHandle.getCutType()==0? RoundingMode.HALF_UP:RoundingMode.HALF_DOWN).add(BigDecimal.ONE);

        // 计算比例及分摊金额
        for(InvoicedApplyItemDTO dto: list){
            BigDecimal sn = dto.getStatementNum();
            if(sn.compareTo(BigDecimal.ZERO) > 0 && 1 == dto.getShareFlag()){
                // 计算分摊比例 -> ntRatio:未税金额比例;
                BigDecimal ntRatio = BigDecimal.ZERO;
                if(nta.compareTo(BigDecimal.ZERO)!=0){
                    ntRatio = dto.getNotTaxAmount().divide(nta, priceHandle.getPrecision(), priceHandle.getCutType()==0?RoundingMode.HALF_UP:RoundingMode.HALF_DOWN);
                }
                if(sn.compareTo(dto.getShareNum()) != 0){
                    ntRatio = dto.getShareNum().multiply(dto.getNotTaxPrice()).divide(nta, priceHandle.getPrecision(), priceHandle.getCutType()==0?RoundingMode.HALF_UP:RoundingMode.HALF_DOWN);
                }

                // 计算分摊金额
                // TODO 2020/9/4 期望分摊金额小于1时该行不分摊
                dto.setShareRntAmount(rna.multiply(ntRatio).setScale(2, BigDecimal.ROUND_HALF_UP));
                dto.setShareRebateAmount(dto.getShareRntAmount().multiply(tax).setScale(2, BigDecimal.ROUND_HALF_UP));
                dto.setShareDntAmount(dna.multiply(ntRatio).setScale(2, BigDecimal.ROUND_HALF_UP));
                dto.setShareDeductionAmount(dto.getShareDntAmount().multiply(tax).setScale(2, BigDecimal.ROUND_HALF_UP));
            }
        }

        // 处理尾差
        // 分摊后明细汇总 -> 返利含税金额; 返利未税金额; 扣款含税金额; 扣款未税金额;
        BigDecimal rtaSum = BigDecimal.ZERO, rnaSum = BigDecimal.ZERO, dtaSum = BigDecimal.ZERO, dnaSum = BigDecimal.ZERO;
        for(InvoicedApplyItemDTO dto: list){
            rtaSum = rtaSum.add(dto.getShareRebateAmount());
            rnaSum = rnaSum.add(dto.getShareRntAmount());
            dtaSum = dtaSum.add(dto.getShareDeductionAmount());
            dnaSum = dnaSum.add(dto.getShareDntAmount());
        }

        // 处理返利含税金额尾差
        if(rta.compareTo(rtaSum) != 0){
            BigDecimal diff = rta.subtract(rtaSum);
            for(InvoicedApplyItemDTO dto: list){
                BigDecimal addDiff = dto.getShareRebateAmount().add(diff);
                // 尾差处理后入库凭证金额不能小于0
                BigDecimal saIta = dto.getTaxAmount().add(addDiff).add(dto.getShareDeductionAmount());
                if(dto.getStatementNum().compareTo(BigDecimal.ZERO) > 0 && 1 == dto.getShareFlag() && saIta.compareTo(BigDecimal.ZERO) > 0){
                    dto.setShareRebateAmount(addDiff);
                    break;
                }
            }
        }

        // 处理返利未税金额尾差
        if(rna.compareTo(rnaSum) != 0){
            BigDecimal diff = rna.subtract(rnaSum);
            for(InvoicedApplyItemDTO dto: list){
                BigDecimal addDiff = dto.getShareRntAmount().add(diff);
                // 尾差处理后入库凭证金额不能小于0
                BigDecimal saNta = dto.getNotTaxAmount().add(addDiff).add(dto.getShareDntAmount());
                if(dto.getStatementNum().compareTo(BigDecimal.ZERO) > 0 && 1 == dto.getShareFlag() && saNta.compareTo(BigDecimal.ZERO) > 0){
                    dto.setShareRntAmount(addDiff);
                    break;
                }
            }
        }

        // 处理扣款含税金额尾差
        if(dta.compareTo(dtaSum) != 0){
            BigDecimal diff = dta.subtract(dtaSum);
            for(InvoicedApplyItemDTO dto: list){
                BigDecimal addDiff = dto.getShareDeductionAmount().add(diff);
                // 尾差处理后入库凭证金额不能小于0
                BigDecimal saIta = dto.getTaxAmount().add(addDiff).add(dto.getShareRebateAmount());
                if(dto.getStatementNum().compareTo(BigDecimal.ZERO) > 0 && 1 == dto.getShareFlag() && saIta.compareTo(BigDecimal.ZERO) > 0){
                    dto.setShareDeductionAmount(addDiff);
                    break;
                }
            }
        }

        // 处理扣款未税金额尾差
        if(dna.compareTo(dnaSum) != 0){
            BigDecimal diff = dna.subtract(dnaSum);
            for(InvoicedApplyItemDTO dto: list){
                BigDecimal addDiff = dto.getShareDntAmount().add(diff);
                // 尾差处理后入库凭证金额不能小于0
                BigDecimal saNta = dto.getNotTaxAmount().add(addDiff).add(dto.getShareRntAmount());
                if(dto.getStatementNum().compareTo(BigDecimal.ZERO) > 0 && 1 == dto.getShareFlag() && saNta.compareTo(BigDecimal.ZERO) > 0){
                    dto.setShareDntAmount(addDiff);
                    break;
                }
            }
        }

        // 获取分摊后入库凭证的相关金额以及分摊税金
        // 分摊后入库凭证的相关金额 -> 分摊后未税金额; 分摊后含税金额; 分摊后税金;
        for(InvoicedApplyItemDTO dto: list){
            // 返利分摊税金
            dto.setSraTaxAmount(dto.getShareRebateAmount().subtract(dto.getShareRntAmount()));
            // 扣款分摊税金
            dto.setSdaTaxAmount(dto.getShareDeductionAmount().subtract(dto.getShareDntAmount()));
            // 分摊后未税金额
            BigDecimal shareAfterNta = dto.getNotTaxAmount().add(dto.getShareRntAmount()).add(dto.getShareDntAmount());
            // 分摊后含税金额
            BigDecimal shareAfterIta = dto.getTaxAmount().add(dto.getShareRebateAmount()).add(dto.getShareDeductionAmount());
            dto.setShareAfterNta(shareAfterNta);
            dto.setShareAfterIta(shareAfterIta);
            dto.setShareAfterTa(shareAfterIta.subtract(shareAfterNta));
        }
    }
}
