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.StringUtils;
import org.springframework.stereotype.Service;
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.stream.Collectors;

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

//    @Async("commonAsyncExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void commissionCompareBatch(List<Commission> entities) {
        // 根据是否有保单号，来区分比对方式
        List<Commission> hasPolicyNoList = new ArrayList<>();
        List<Commission> noPolicyNoList = new ArrayList<>();
        for (Commission entity : entities) {
            if (StringUtils.isBlank(entity.getPolicyNo())) {
                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());
        // 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())
                        .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.根据导入的来佣获取保单号集合
        List<String> policyNoList = entities.stream()
                .map(Commission::getPolicyNo)
                .collect(Collectors.toList());

        // 2.根据保单号查询已存在的来佣记录
        List<Commission> existingCommissions = commissionService.lambdaQuery()
                .in(Commission::getPolicyNo, policyNoList)
                .list();
        // 来佣根据 保单号、来佣名称、来佣周期、货币 进行分组
        Map<String, List<Commission>> commissionMap = existingCommissions.stream()
                .collect(Collectors.groupingBy(
                        c -> c.getPolicyNo() + c.getCommissionName() + c.getCommissionPeriod() + c.getCurrency()
                ));

        // 3.根据保单号查询预计来佣
        List<CommissionExpected> expectedList = commissionExpectedService.lambdaQuery()
                .in(CommissionExpected::getPolicyNo, policyNoList)
                .list();
        // 预计来佣根据 保单号、来佣名称、来佣周期、货币 进行映射
        Map<String, CommissionExpected> expectedMap = expectedList.stream()
                .collect(Collectors.toMap(
                        commissionExpected -> commissionExpected.getPolicyNo() + commissionExpected.getCommissionName() + commissionExpected.getCommissionPeriod() + commissionExpected.getCurrency(),
                        commissionExpected -> commissionExpected
                ));

        // 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) {
            if (item.getCurrentCommissionRatio() == null) {
                log.warn("当前来佣比例为空，临时计算当前来佣比例: commission_id={}", item.getId());
                paidRatio = paidRatio.add(item.calculateCurrentPaidRatio());
            } else {
                paidRatio = paidRatio.add(item.getCurrentCommissionRatio());
            }
            paidAmount = paidAmount.add(item.getAmount());
        }

        // 更新预计来佣已入账金额、已入账比例
        commissionExpected.setPaidAmount(paidAmount);
        commissionExpected.setPaidRatio(paidRatio);

        // endregion 计算预计来佣属性

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

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

        // 计算比对状态
        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.setScale(2, RoundingMode.HALF_UP))
                    .set(CommissionExpected::getPaidRatio, paidRatio)
                    .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())
                    .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 (StringUtils.isNotBlank(commission.getPolicyNo())) {
            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_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) {
            // 等于预计金额时，来佣设置为比对成功
            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())
                    .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)
                .eq("currency", currency));

        // 当前 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 = commissionService.queryByCommission(
                commission.getPolicyNo(),
                commission.getCommissionName(),
                commission.getCommissionPeriod(),
                commission.getCurrency(),
                premium);

        doCompare(existingCommissions, commissionExpected, commission);
    }

    private void doCompare(List<Commission> existingCommissions, CommissionExpected commissionExpected, Commission commission) {
        // 校验参数
        if (commission.getTotalPeriod().compareTo(commissionExpected.getTotalPeriod()) != 0) {
            commission.setRemark("来佣总期数与预计来佣总期数不一致");
            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.getAmount());
        }

        // 更新预计来佣已入账金额、已入账比例
        commissionExpected.setPaidAmount(paidAmount);
        commissionExpected.setPaidRatio(paidRatio);

        // 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)
                    .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())
                    .eq(CommissionExpected::getId, commissionExpected.getId())
                    .update();
            // 保存比对记录
            commissionService.saveCompareRecord(commission, commissionExpected, currentLoginUser);
        }
    }
}
