package com.yd.csf.service.component;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
import com.yd.csf.service.enums.CommissionExpectedStatusEnum;
import com.yd.csf.service.enums.CommissionStatusEnum;
import com.yd.csf.service.model.Commission;
import com.yd.csf.service.model.CommissionCompareRecord;
import com.yd.csf.service.model.CommissionExpected;
import com.yd.csf.service.service.CommissionCompareRecordService;
import com.yd.csf.service.service.CommissionExpectedService;
import com.yd.csf.service.service.CommissionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CommissionAsyncService {
    @Resource
    private CommissionExpectedService commissionExpectedService;
    @Resource
    private CommissionService commissionService;
    @Resource
    private CommissionCompareRecordService commissionCompareRecordService;

    //    @Async("commonAsyncExecutor")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void commissionCompareBatch(List<Commission> entities) {
        // 根据是否关联保单号，来区分比对方式
        List<Commission> hasPolicyNoList = new ArrayList<>();
        List<Commission> noPolicyNoList = new ArrayList<>();
        for (Commission entity : entities) {
            if ("U".equals(entity.getCommissionBizType())) {
                noPolicyNoList.add(entity);
            } else {
                hasPolicyNoList.add(entity);
            }
        }
        if (CollectionUtils.isNotEmpty(hasPolicyNoList)) {
            normalBatch(hasPolicyNoList);
        }
        if (CollectionUtils.isNotEmpty(noPolicyNoList)) {
            noPolicyNoBatch(noPolicyNoList);
        }
    }

    /**
     * 无保单号的来佣比对
     *
     * @param noPolicyNoList
     */
    private void noPolicyNoBatch(List<Commission> noPolicyNoList) {
        // 1.根据导入的来佣获取应收款编号集合
        List<String> receivableNoList = noPolicyNoList.stream()
                .map(Commission::getReceivableNo)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(receivableNoList)) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "非关联保单号应收单，应收款编号不能为空");
        }
        // 2.根据应收款编号查询预计来佣
        List<CommissionExpected> expectedList = commissionExpectedService.lambdaQuery()
                .in(CommissionExpected::getReceivableNo, receivableNoList)
                .list();
        // 3.根据应收款编号映射预计来佣
        Map<String, CommissionExpected> expectedMap = expectedList.stream()
                .collect(Collectors.toMap(
                        CommissionExpected::getReceivableNo,
                        commissionExpected -> commissionExpected
                ));

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 比对记录
        List<CommissionCompareRecord> compareRecords = new ArrayList<>();

        // 4.遍历导入的来佣，进行比对
        for (Commission commission : noPolicyNoList) {
            // 根据应收款编号获取预计来佣
            CommissionExpected commissionExpected = expectedMap.get(commission.getReceivableNo());
            if (commissionExpected == null) {
                continue;
            }
            // 比对来佣金额是否一致
            BigDecimal expectedAmount = commissionExpected.getAmount();
            BigDecimal actualAmount = commission.getAmount();
            if (expectedAmount.equals(actualAmount) && commissionExpected.getCurrency().equals(commission.getCurrency())) {
                // 创建比对记录
                compareRecords.add(commissionService.getNewCompareRecord(commission, commissionExpected, currentLoginUser));
                // 对应预计来佣设置为已来佣、待入账金额设置为 0
                commissionExpectedService.lambdaUpdate()
                        .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.COMPARED.getItemValue())
                        .set(CommissionExpected::getPaidAmount, BigDecimal.ZERO)
                        .eq(CommissionExpected::getId, commissionExpected.getId())
                        .update();
                // 来佣设置为比对成功
                commissionService.lambdaUpdate()
                        .set(Commission::getStatus, CommissionStatusEnum.COMPARE_SUCCESS.getItemValue())
                        .eq(Commission::getId, commission.getId())
                        .update();
            } else {
                // 预计来佣设置为部分来佣
                commissionExpectedService.lambdaUpdate()
                        .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.PARTIAL.getItemValue())
                        .set(CommissionExpected::getPaidAmount, actualAmount)
                        .eq(CommissionExpected::getId, commissionExpected.getId())
                        .update();
                // 来佣比对失败
                commissionService.lambdaUpdate()
                        .set(Commission::getStatus, CommissionStatusEnum.COMPARE_FAIL.getItemValue())
                        .eq(Commission::getId, commission.getId())
                        .update();
                // 创建比对记录
                compareRecords.add(commissionService.getNewCompareRecord(commission, commissionExpected, currentLoginUser));
            }
        }
        // 保存比对记录
        if (CollectionUtils.isNotEmpty(compareRecords)) {
            commissionCompareRecordService.saveBatch(compareRecords);
        }
    }

    private void normalBatch(List<Commission> entities) {
        // 1.需要比对的预计来佣业务id集合
        List<String> commissionExpectedBizIdList = entities.stream()
                .map(Commission::getCommissionExpectedBizId)
                .collect(Collectors.toList());
        // 2.根据预计来佣业务id查询已存在的来佣记录
        List<Commission> existingCommissions = commissionService.lambdaQuery()
                .in(Commission::getCommissionExpectedBizId, commissionExpectedBizIdList)
                .list();
        // commission 根据关联的 commission_expected_biz_id 进行分组
        Map<String, List<Commission>> commissionMap = existingCommissions.stream()
                .filter(commission -> commission.getCommissionExpectedBizId() != null)
                .collect(Collectors.groupingBy(Commission::getCommissionExpectedBizId));

        // 3.根据保单号查询预计来佣
        List<CommissionExpected> expectedList = commissionExpectedService.lambdaQuery()
                .in(CommissionExpected::getCommissionExpectedBizId, commissionExpectedBizIdList)
                .list();
        // 预计来佣映射
        Map<String, CommissionExpected> expectedMap = expectedList.stream()
                .collect(Collectors.toMap(CommissionExpected::getCommissionExpectedBizId, Function.identity()));

        // 4.遍历预计来佣，进行比对
        for (Map.Entry<String, CommissionExpected> entry : expectedMap.entrySet()) {
            String key = entry.getKey();
            CommissionExpected commissionExpected = entry.getValue();
            // 从已存在的来佣记录中获取当前来佣记录
            List<Commission> existingCommissionsByKey = commissionMap.getOrDefault(key, new ArrayList<>());
            // 进行比对
            doCompareBatch(commissionExpected, existingCommissionsByKey);
        }
    }

    private void doCompareBatch(CommissionExpected commissionExpected, List<Commission> existingCommissions) {
        // 校验参数
        boolean validFlag = false;
        for (Commission existingCommission : existingCommissions) {
            Integer commissionTotalPeriod = existingCommission.getTotalPeriod();
            Integer expectedTotalPeriod = commissionExpected.getTotalPeriod();
            if (commissionTotalPeriod.compareTo(expectedTotalPeriod) != 0) {
                existingCommission.setRemark("来佣总期数与预计来佣总期数不一致");
                existingCommission.setStatus(CommissionStatusEnum.COMPARE_FAIL.getItemValue());
                validFlag = true;
            }
        }
        // 参数校验未通过，更新后，不执行后续计算
        if (validFlag) {
            commissionService.updateBatchById(existingCommissions);
            return;
        }

        // region 计算预计来佣属性

        // 统计已入账金额、已入账比例
        BigDecimal paidRatio = BigDecimal.ZERO;
        BigDecimal paidAmount = BigDecimal.ZERO;
        for (Commission item : existingCommissions) {
            paidRatio = paidRatio.add(item.getCurrentCommissionRatio());
            paidAmount = paidAmount.add(item.getHkdAmount());
        }

        // 更新预计来佣已入账金额、已入账比例、达成率缺口
        commissionExpected.setPaidAmount(paidAmount);
        commissionExpected.setPaidRatio(paidRatio);
        commissionExpected.setPendingRatio(
                BigDecimal.ONE
                        .subtract(paidRatio.divide(commissionExpected.getCommissionRatio(), 4, RoundingMode.HALF_UP))
                        .multiply(BigDecimal.valueOf(100))
        );


        // endregion 计算预计来佣属性

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 比对记录
        List<CommissionCompareRecord> compareRecords = new ArrayList<>();

        // 计算比对状态
        log.info("paidRatio: {}, commissionExpected.getCommissionRatio(): {}", paidRatio, commissionExpected.getCommissionRatio());
        log.info("commissionExpected_id: {}", commissionExpected.getId());
        if (paidRatio.compareTo(commissionExpected.getCommissionRatio()) == 0) {
            // 等于预计比例时，所有来佣设置为比对成功
            for (Commission existingCommission : existingCommissions) {
                existingCommission.setStatus(CommissionStatusEnum.COMPARE_SUCCESS.getItemValue());
                existingCommission.setTotalPeriod(commissionExpected.getTotalPeriod());
                // 创建比对记录
                compareRecords.add(commissionService.getNewCompareRecord(existingCommission, commissionExpected, currentLoginUser));
            }
            // 更新预计来佣状态为已来佣、已入账金额、已入账比例
            commissionExpectedService.lambdaUpdate()
                    .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.COMPARED.getItemValue())
                    .set(CommissionExpected::getPaidAmount, paidAmount)
                    .set(CommissionExpected::getPaidRatio, paidRatio)
                    .set(CommissionExpected::getPendingRatio, commissionExpected.getPendingRatio())
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 更新已比对来佣记录
            if (CollectionUtils.isNotEmpty(existingCommissions)) {
                commissionService.updateStatusBatchById(existingCommissions);
            }
            // 保存比对记录
            commissionCompareRecordService.saveBatch(compareRecords);
        } else {
            // 比对失败时，所有来佣设置为比对失败
            for (Commission existingCommission : existingCommissions) {
                existingCommission.setStatus(CommissionStatusEnum.COMPARE_FAIL.getItemValue());
                // 创建比对记录
                compareRecords.add(commissionService.getNewCompareRecord(existingCommission, commissionExpected, currentLoginUser));
            }
            // 对应预计来佣设置为部分来佣
            commissionExpectedService.lambdaUpdate()
                    .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.PARTIAL.getItemValue())
                    .set(CommissionExpected::getPaidAmount, paidAmount)
                    .set(CommissionExpected::getPaidRatio, paidRatio)
                    .set(CommissionExpected::getPendingRatio, commissionExpected.getPendingRatio())
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 更新已比对来佣记录
            if (CollectionUtils.isNotEmpty(existingCommissions)) {
                commissionService.updateStatusBatchById(existingCommissions);
            }
            // 保存比对记录
            commissionCompareRecordService.saveBatch(compareRecords);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void commissionCompare(Commission commission) {
        log.info("开始执行比对事务，来佣ID: {}", commission.getCommissionBizId());

        // 未查询到预计来佣，需要先同步到预计来佣表
        if (CommissionStatusEnum.MATCH_FAILED.getItemValue().equals(commission.getStatus())) {
            return;
        }

        if ("R".equals(commission.getCommissionBizType())) {
            normalCommission(commission.getCommissionBizId(), commission.getPolicyNo(), commission.getCommissionName(), commission.getCommissionPeriod(), commission.getCurrency(), commission.getPremium());
        } else {
            // 非关联保单号应收单，根据来佣名称、对账公司查询来佣记录
            noPolicyNoCommission(commission);
        }
    }

    private void noPolicyNoCommission(Commission commission) {
        if (commission.getAmount() == null) {
            commission.setRemark("来佣金额不能为空");
            commissionService.updateById(commission);
            return;
        }

        // 1.根据来佣名称、对账公司查询预计来佣
        QueryWrapper<CommissionExpected> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("reconciliation_company", commission.getReconciliationCompany());
        queryWrapper.eq("commission_period", commission.getCommissionPeriod());
        queryWrapper.eq("commission_name", commission.getCommissionName());
        List<CommissionExpected> expectedList = commissionExpectedService.list(queryWrapper);
        if (expectedList.size() != 1) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "该预计来佣记录不存在或存在多条");
        }
        CommissionExpected commissionExpected = expectedList.get(0);

        // 更新预计来佣已入账金额
        commissionExpected.setPaidAmount(commission.getAmount());

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 计算比对状态
        if (commission.getAmount().compareTo(commissionExpected.getAmount()) == 0 && ObjectUtils.equals(commission.getCurrency(), commissionExpected.getCurrency())) {
            // 等于预计金额时，来佣设置为比对成功
            commissionService.lambdaUpdate()
                    .set(Commission::getStatus, CommissionStatusEnum.COMPARE_SUCCESS.getItemValue())
                    .eq(Commission::getId, commission.getId())
                    .update();
            // 预计来佣设置为已来佣、待入账金额设置为 0
            commissionExpectedService.lambdaUpdate()
                    .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.COMPARED.getItemValue())
                    .set(CommissionExpected::getPaidAmount, BigDecimal.ZERO)
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 保存比对记录
            commissionService.saveCompareRecord(commission, commissionExpected, currentLoginUser);
        } else {
            // 不等于预计比例时，设置为比对失败
            commissionService.lambdaUpdate()
                    .set(Commission::getStatus, CommissionStatusEnum.COMPARE_FAIL.getItemValue())
                    .eq(Commission::getId, commission.getId())
                    .update();
            // 预计来佣设置为部分来佣
            commissionExpectedService.lambdaUpdate()
                    .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.PARTIAL.getItemValue())
                    .set(CommissionExpected::getPaidAmount, commission.getAmount())
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 保存比对记录
            commissionService.saveCompareRecord(commission, commissionExpected, currentLoginUser);
        }

    }

    private void normalCommission(String commissionBizId, String policyNo, String commissionName, Integer commissionPeriod, String currency, String premium) {
        // 查询所有已存在的来佣记录
        List<Commission> existingCommissions = commissionService.list(new QueryWrapper<Commission>()
                .eq("policy_no", policyNo)
                .eq("commission_name", commissionName)
                .eq("commission_period", commissionPeriod));

        // 当前 commission 记录
        Commission commission = existingCommissions.stream()
                .filter(c -> c.getCommissionBizId().equals(commissionBizId))
                .findFirst()
                .orElse(null);
        if (commission == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "未找到该来佣记录");
        }

        // 查询对应预计来佣
        CommissionExpected commissionExpected = commissionExpectedService.getByBizId(commission.getCommissionExpectedBizId());

        doCompare(existingCommissions, commissionExpected, commission);
    }

    private void doCompare(List<Commission> existingCommissions, CommissionExpected commissionExpected, Commission commission) {
        // 校验参数
        if (commission.getTotalPeriod().compareTo(commissionExpected.getTotalPeriod()) != 0) {
            commission.setRemark("来佣总期数与预计来佣总期数不一致");
            commission.setStatus(CommissionStatusEnum.COMPARE_FAIL.getItemValue());
            commissionService.updateById(commission);
            return;
        }

        // region 计算预计来佣属性

        // 统计已入账金额、已入账比例
        BigDecimal paidRatio = BigDecimal.ZERO;
        BigDecimal paidAmount = BigDecimal.ZERO;
        for (Commission item : existingCommissions) {
            paidRatio = paidRatio.add(item.getCurrentCommissionRatio());
            paidAmount = paidAmount.add(item.getHkdAmount());
        }

        // 更新预计来佣已入账金额、已入账比例
        commissionExpected.setPaidAmount(paidAmount);
        commissionExpected.setPaidRatio(paidRatio);
        commissionExpected.setPendingRatio(
                BigDecimal.ONE
                        .subtract(paidRatio.divide(commissionExpected.getCommissionRatio(), 4, RoundingMode.HALF_UP))
                        .multiply(BigDecimal.valueOf(100))
        );

        // endregion 计算预计来佣属性

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 计算比对状态
        if (paidRatio.compareTo(commissionExpected.getCommissionRatio()) == 0) {
            // 等于预计比例时，所有来佣设置为比对成功
            for (Commission existingCommission : existingCommissions) {
                existingCommission.setStatus(CommissionStatusEnum.COMPARE_SUCCESS.getItemValue());
                existingCommission.setTotalPeriod(commissionExpected.getTotalPeriod());
            }
            // 对应预计来佣设置为已来佣、待入账金额设置为 0
            commissionExpectedService.lambdaUpdate()
                    .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.COMPARED.getItemValue())
                    .set(CommissionExpected::getPaidAmount, paidAmount.setScale(4, RoundingMode.HALF_UP))
                    .set(CommissionExpected::getPaidRatio, paidRatio)
                    .set(CommissionExpected::getPendingRatio, commissionExpected.getPendingRatio())
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 更新已比对来佣记录
            if (CollectionUtils.isNotEmpty(existingCommissions)) {
                commissionService.updateStatusBatchById(existingCommissions);
            }
            // 保存比对记录
            commissionService.saveCompareRecord(commission, commissionExpected, currentLoginUser);
        } else {
            // 不等于预计比例时，设置为比对失败
            commissionService.lambdaUpdate()
                    .set(Commission::getStatus, CommissionStatusEnum.COMPARE_FAIL.getItemValue())
                    .eq(Commission::getId, commission.getId())
                    .update();
            // 预计来佣设置为部分来佣
            commissionExpectedService.lambdaUpdate()
                    .set(CommissionExpected::getStatus, CommissionExpectedStatusEnum.PARTIAL.getItemValue())
                    .set(CommissionExpected::getPaidAmount, paidAmount)
                    .set(CommissionExpected::getPaidRatio, paidRatio)
                    .set(CommissionExpected::getPendingRatio, commissionExpected.getPendingRatio())
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 保存比对记录
            commissionService.saveCompareRecord(commission, commissionExpected, currentLoginUser);
        }
    }
}
