package com.yd.csf.service.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yd.common.enums.CommonEnum;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.csf.service.dto.*;
import com.yd.csf.service.enums.CommissionExpectedStatusEnum;
import com.yd.csf.service.enums.CommissionStatusEnum;
import com.yd.csf.service.enums.FortuneStatusEnum;
import com.yd.csf.service.model.*;
import com.yd.csf.service.service.*;
import com.yd.csf.service.dao.CommissionMapper;
import com.yd.csf.service.vo.CommissionStatisticsVO;
import com.yd.csf.service.vo.CommissionVO;
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.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Zhang Jianan
 * @description 针对表【commission(保单来佣表)】的数据库操作Service实现
 * @createDate 2025-09-19 16:08:05
 */
@Service
@Slf4j
public class CommissionServiceImpl extends ServiceImpl<CommissionMapper, Commission>
        implements CommissionService {

    @Resource
    private FortuneService fortuneService;
    @Resource
    private PolicyFollowService policyFollowService;
    @Resource
    private PolicyBrokerService policyBrokerService;
    @Resource
    private PolicyService policyService;
    @Resource
    private CommissionExpectedService commissionExpectedService;
    @Resource
    private IExpectedFortuneService iExpectedFortuneService;
    @Resource
    private CommissionEditRecordService commissionEditRecordService;
    @Resource
    private CommissionCompareRecordService commissionCompareRecordService;
    @Resource
    private CommissionAsyncService commissionAsyncService;
    @Resource
    private TransactionTemplate transactionTemplate;


    @Override
    public QueryWrapper<Commission> getQueryWrapper(CommissionQueryRequest commissionQueryRequest) {
        QueryWrapper<Commission> queryWrapper = new QueryWrapper<>();
        if (commissionQueryRequest == null) {
            return queryWrapper;
        }

        String reconciliationYearMonth = commissionQueryRequest.getReconciliationYearMonth();
        List<String> statusList = commissionQueryRequest.getStatusList();
        String policyNo = commissionQueryRequest.getPolicyNo();
        List<String> insuranceCompanyBizIdList = commissionQueryRequest.getInsuranceCompanyBizIdList();
        List<String> reconciliationCompanyBizIdList = commissionQueryRequest.getReconciliationCompanyBizIdList();
        Date commissionDateStart = commissionQueryRequest.getCommissionDateStart();
        Date commissionDateEnd = commissionQueryRequest.getCommissionDateEnd();
        Date expectedDate = commissionQueryRequest.getExpectedDate();

        // 查询对账年月
        queryWrapper.eq(StringUtils.isNotBlank(reconciliationYearMonth), "reconciliation_year_month", reconciliationYearMonth);

        queryWrapper.in(CollectionUtils.isNotEmpty(statusList), "status", statusList);
        queryWrapper.like(StringUtils.isNotBlank(policyNo), "policy_no", policyNo);
        queryWrapper.in(CollectionUtils.isNotEmpty(insuranceCompanyBizIdList), "insurance_company_biz_id", insuranceCompanyBizIdList);
        queryWrapper.in(CollectionUtils.isNotEmpty(reconciliationCompanyBizIdList), "reconciliation_company_biz_id", reconciliationCompanyBizIdList);

        if (commissionDateStart != null && commissionDateEnd != null) {
            queryWrapper.between("commission_date", commissionDateStart, commissionDateEnd);
        }
        // 查询预计入账日期及之前的记录, 关联查询 commission_expected 表
        if (expectedDate != null) {
            queryWrapper.apply("EXISTS (SELECT 1 FROM commission_expected ce WHERE ce.commission_expected_biz_id = commission.commission_expected_biz_id AND ce.commission_date <= {0})",
                            expectedDate);
        }

        queryWrapper.orderByDesc("id");
        return queryWrapper;
    }

    @Override
    public Page<CommissionVO> getCommissionVOPage(Page<Commission> commissionPage) {
        List<Commission> commissionList = commissionPage.getRecords();
        Page<CommissionVO> commissionVOPage = new Page<>(commissionPage.getCurrent(), commissionPage.getSize(), commissionPage.getTotal());
        if (CollUtil.isEmpty(commissionList)) {
            return commissionVOPage;
        }

        List<CommissionVO> commissionVOList = commissionList.stream().map(CommissionVO::objToVo).collect(Collectors.toList());

        commissionVOPage.setRecords(commissionVOList);
        return commissionVOPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateCommission(CommissionUpdateRequest commissionUpdateRequest) {
        String commissionBizId = commissionUpdateRequest.getCommissionBizId();

        // 查询旧数据
        Commission commission = this.getByCommissionBizId(commissionBizId);
        if (commission == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "未找到该来佣记录");
        }

        BigDecimal amount = commission.getAmount();
        BigDecimal requestAmount = commissionUpdateRequest.getAmount();

        // 1. 执行主更新事务（这个方法将在当前事务中执行）
        // 计算当前来佣比例
        BigDecimal currentCommissionRatio = this.calculateCurrentCommissionRatio(
                commissionUpdateRequest.getAmount(),
                commissionUpdateRequest.getPremium(),
                commissionUpdateRequest.getExchangeRate());

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

        // 保存修改记录
        saveUpdateCommissionRecord(commission, commissionUpdateRequest, currentLoginUser);

        // 更新属性
        BeanUtils.copyProperties(commissionUpdateRequest, commission, "commissionBizId");
        commission.setCurrentCommissionRatio(currentCommissionRatio);
        commission.setUpdaterId(loginUserId);
        commission.setUpdateTime(new Date());
        // 执行更新操作（这个方法将在当前事务中执行）
        this.updateById(commission);

        // 没改金额，则不比对
        if (amount.compareTo(requestAmount) != 0) {
            // 2. 注册事务同步器，在主事务提交后执行比对
            TransactionSynchronizationManager.registerSynchronization(
                    new TransactionSynchronization() {
                        @Override
                        public void afterCommit() {
                            try {
                                // 重新查询最新的数据，获取已提交的数据
                                commissionAsyncService.commissionCompare(commission);
                            } catch (Exception e) {
                                // 比对失败不影响主事务，记录日志即可
                                log.error("主事务提交后，比对操作执行失败，commissionBizId: {}",
                                        commissionUpdateRequest.getCommissionBizId(), e);
                            }
                        }
                    }
            );
        }
        return true;
    }

    @Override
    public void validateCommissionUpdateRequest(CommissionUpdateRequest commissionUpdateRequest) {
        // 校验入参
        if (ObjectUtils.isEmpty(commissionUpdateRequest.getCommissionBizId())) {
            log.error("来佣业务id不能为空：{}", commissionUpdateRequest.getCommissionBizId());
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "来佣业务id不能为空");
        }
        if (ObjectUtils.isEmpty(commissionUpdateRequest.getPremium())) {
            log.error("保费不能为空：{}", commissionUpdateRequest.getPremium());
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "保费不能为空");
        }
        if (ObjectUtils.isEmpty(commissionUpdateRequest.getAmount())) {
            log.error("当前入账金额不能为空，来佣业务id：{}", commissionUpdateRequest.getCommissionBizId());
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "当前入账金额不能为空");
        }
        if (ObjectUtils.isEmpty(commissionUpdateRequest.getExchangeRate())) {
            log.error("当前结算汇率不能为空，来佣业务id：{}", commissionUpdateRequest.getCommissionBizId());
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "当前结算汇率不能为空");
        }
    }


    @Override
    public void saveUpdateCommissionRecord(Commission commission, CommissionUpdateRequest commissionUpdateRequest, AuthUserDto currentLoginUser) {
        // 保存修改记录
        List<CommissionEditRecord> commissionEditRecords = new ArrayList<>();

        if (!Objects.equals(commission.getPolicyNo(), commissionUpdateRequest.getPolicyNo())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("保单号", commission.getPolicyNo(), commissionUpdateRequest.getPolicyNo(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getReconciliationCompany(), commissionUpdateRequest.getReconciliationCompany())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("对账公司", commission.getReconciliationCompany(), commissionUpdateRequest.getReconciliationCompany(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getCommissionPeriod(), commissionUpdateRequest.getCommissionPeriod())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("佣金期数", commission.getCommissionPeriod(), commissionUpdateRequest.getCommissionPeriod(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getTotalPeriod(), commissionUpdateRequest.getTotalPeriod())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("总来佣期数", commission.getTotalPeriod(), commissionUpdateRequest.getTotalPeriod(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getCommissionName(), commissionUpdateRequest.getCommissionName())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("来佣名称", commission.getCommissionName(), commissionUpdateRequest.getCommissionName(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (commission.getAmount().compareTo(commissionUpdateRequest.getAmount()) != 0) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("来佣金额", commission.getAmount(), commissionUpdateRequest.getAmount(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getCurrency(), commissionUpdateRequest.getCurrency())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("币种", commission.getCurrency(), commissionUpdateRequest.getCurrency(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getExchangeRate(), commissionUpdateRequest.getExchangeRate())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("结算汇率", commission.getExchangeRate(), commissionUpdateRequest.getExchangeRate(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        String commissionDate = DateUtil.formatDate(commission.getCommissionDate());
        String requestCommissionDate = DateUtil.formatDate(commissionUpdateRequest.getCommissionDate());
        if (!Objects.equals(commissionDate, requestCommissionDate)) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("来佣日期", commissionDate, requestCommissionDate, commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getStatus(), commissionUpdateRequest.getStatus())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("来佣比对状态", commission.getStatus(), commissionUpdateRequest.getStatus(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }
        if (!Objects.equals(commission.getRemark(), commissionUpdateRequest.getRemark())) {
            CommissionEditRecord commissionRecord = getCommissionEditRecord("备注", commission.getRemark(), commissionUpdateRequest.getRemark(), commission.getCommissionBizId(), currentLoginUser);
            commissionEditRecords.add(commissionRecord);
        }

        if (CollectionUtils.isNotEmpty(commissionEditRecords)) {
            // 变更序号从1开始
            commissionEditRecords.forEach(commissionRecord -> commissionRecord.setSeq(commissionEditRecords.indexOf(commissionRecord) + 1));
            commissionEditRecordService.saveBatch(commissionEditRecords);
        }
    }

    private static CommissionEditRecord getCommissionEditRecord(String field, Object commissionPropertyValue, Object requestPropertyValue, String commissionBizId, AuthUserDto currentLoginUser) {
        CommissionEditRecord commissionRecord = new CommissionEditRecord();

        commissionRecord.setField(field);
        commissionRecord.setBeforeChange(commissionPropertyValue);
        commissionRecord.setAfterChange(requestPropertyValue);

        commissionRecord.setCommissionBizId(commissionBizId);
        commissionRecord.setUserBizId(currentLoginUser.getId().toString());
        commissionRecord.setUserName(currentLoginUser.getUsername());
        commissionRecord.setCreateTime(new Date());
        return commissionRecord;
    }

    @Override
    public CommissionExpected queryByCommission(String policyNo, String commissionName, Integer commissionPeriod, String currency, String premium) {
        QueryWrapper<CommissionExpected> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("policy_no", policyNo);
        queryWrapper.eq("commission_name", commissionName);
        queryWrapper.eq("commission_period", commissionPeriod);
        queryWrapper.eq("currency", currency);
        CommissionExpected one = commissionExpectedService.getOne(queryWrapper);
        if (ObjectUtils.isEmpty(one)) {
            String errorMsg = String.format("未找到当前来佣对应的预计来佣，保单号：%s, 来佣名称：%s, 来佣期数：%s, 币种：%s",
                    policyNo, commissionName, commissionPeriod, currency);
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), errorMsg);
        }
        // 计算预计来佣金额 (统一试算币种：HKD)
        if (one.getExpectedAmount() == null) {
            BigDecimal expectedAmount = new BigDecimal(premium)
                    .multiply(one.getCommissionRatio());
            if ("USD".equals(one.getCurrency())) {
                expectedAmount = expectedAmount.multiply(one.getDefaultExchangeRate());
            }
            one.setExpectedAmount(expectedAmount.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP));
        }
        return one;
    }

    /**
     * 根据对应的所有来佣记录，统计已入账金额、已入账比例
     *
     * @param commission
     * @param one
     */
    @Override
    public void calculatePaidAmountByCommissionList(Commission commission, CommissionExpected one) {
        // 查询当前来佣对应的所有来佣记录
        QueryWrapper<Commission> queryWrapperCommission = new QueryWrapper<>();
        queryWrapperCommission.eq("policy_no", commission.getPolicyNo());
        queryWrapperCommission.eq("commission_name", commission.getCommissionName());
        queryWrapperCommission.eq("commission_period", commission.getCommissionPeriod());
        queryWrapperCommission.eq("currency", commission.getCurrency());
        List<Commission> commissionList = this.list(queryWrapperCommission);
        // 统计已入账金额、已入账比例
        BigDecimal paidRatio = BigDecimal.ZERO;
        BigDecimal paidAmount = BigDecimal.ZERO;
        for (Commission commission1 : commissionList) {
            if (commission1.getStatus().equals(CommissionStatusEnum.COMPARE_SUCCESS.getItemValue())) {
                paidRatio = paidRatio.add(commission1.getCurrentCommissionRatio());
                paidAmount = paidAmount.add(commission1.getAmount());
                one.setPaidAmount(paidAmount);
                one.setPaidRatio(paidRatio);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean generateFortune(GenerateFortuneRequest generateFortuneRequest) {
        LocalDate payoutYearMonth = generateFortuneRequest.getPayoutYearMonth();
        if (ObjectUtils.isEmpty(payoutYearMonth)) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "请选择预计出账年月");
        }

        // 1. 查询在预计出账年月的预计发佣记录，预计出账日期在预计出账年月的1号到最后一天
        QueryWrapper<ExpectedFortune> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("payout_date", payoutYearMonth.withDayOfMonth(1), payoutYearMonth.withDayOfMonth(payoutYearMonth.lengthOfMonth()));
        List<ExpectedFortune> expectedFortuneList = iExpectedFortuneService.list(queryWrapper);

        if (CollectionUtils.isEmpty(expectedFortuneList)) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "未找到保单对应的预计发佣记录，请先创建预计发佣记录");
        }

        // 2. 根据本次发佣日期，查询本期待发佣记录
        QueryWrapper<Fortune> queryWrapperFortune = new QueryWrapper<>();
        queryWrapperFortune.eq("reconciliation_year_month", payoutYearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")));
        List<Fortune> fortuneList = fortuneService.list(queryWrapperFortune);
        // 2.1 校验是否有已出账的记录
        for (Fortune fortune : fortuneList) {
            if (FortuneStatusEnum.SENT.getItemValue().equals(fortune.getStatus())) {
                throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "已出账的记录不能重新出账");
            }
        }

        // 2.2 过滤掉 is_part = 1 的 fortune 记录
        List<Fortune> filteredFortuneList = fortuneList.stream()
                .filter(fortune -> Integer.valueOf(0).equals(fortune.getIsPart()))
                .collect(Collectors.toList());

        // 3.根据本次发佣日期，删除旧的发佣记录，以便重新计算
        fortuneService.removeByIds(filteredFortuneList);

        // 4.根据保单号查询对应来佣记录
        Set<String> policyNoSet = expectedFortuneList.stream()
                .map(ExpectedFortune::getPolicyNo)
                .collect(Collectors.toSet());

        List<Commission> commissionList = this.list(new QueryWrapper<Commission>().in("policy_no", policyNoSet));

        // 5. 构建实际的初始发佣记录
        List<Fortune> newFortuneList = buildNewFortunes(expectedFortuneList, commissionList, payoutYearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")));
//        List<Fortune> newFortuneList = new ArrayList<>();
//        for (ExpectedFortune expectedFortune : filteredExpectedFortuneList) {
//            Fortune fortune = new Fortune();
//            BeanUtils.copyProperties(expectedFortune, fortune);
//
//            fortune.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
//            fortune.setStatus(FortuneStatusEnum.WAIT.getItemValue());
//            // 关联来佣业务ID
//            matchCommission(fortune, commissionList);
//            // 扣减已发薪资
//            calculateCurrentFortune(fortune, policyPaidFortuneList);
//
//            fortune.setCreateTime(new Date());
//            fortune.setUpdateTime(new Date());
//
//            newFortuneList.add(fortune);
//        }

        // 10. 保存发佣记录
        saveNewFortunes(newFortuneList);

        return true;
    }

    /**
     * 保存新的发佣记录
     */
    private void saveNewFortunes(List<Fortune> newFortuneList) {
        if (CollectionUtils.isEmpty(newFortuneList)) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "没有可保存的发佣记录");
        }

        boolean saveSuccess = fortuneService.saveBatch(newFortuneList);
        if (!saveSuccess) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "保存发佣记录失败");
        }
    }

    /**
     * 构建新的发佣记录
     */
    private List<Fortune> buildNewFortunes(List<ExpectedFortune> expectedFortuneList,
                                           List<Commission> commissionList,
                                           String reconciliationYearMonth) {
        // 构建来佣记录映射，用于快速查找
        Map<String, Commission> commissionByPolicyPeriod = commissionList.stream()
                .collect(Collectors.toMap(
                        commission -> buildPolicyPeriodKey(commission.getPolicyNo(), commission.getCommissionPeriod()),
                        Function.identity()
                ));

        Date now = new Date();
        return expectedFortuneList.stream()
                .map(expectedFortune -> {
                    Fortune fortune = new Fortune();
                    BeanUtils.copyProperties(expectedFortune, fortune);

                    fortune.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
                    fortune.setAmount(expectedFortune.getAmount());
                    fortune.setExpectedFortuneBizId(expectedFortune.getExpectedFortuneBizId());
                    fortune.setStatus(FortuneStatusEnum.CAN_SEND.getItemValue());
                    fortune.setIsPart(0);
                    // 检核日期
                    fortune.setReconciliationYearMonth(reconciliationYearMonth);

                    // 关联来佣业务ID
                    String key = buildPolicyPeriodKey(expectedFortune.getPolicyNo(), expectedFortune.getFortunePeriod());
                    Commission matchedCommission = commissionByPolicyPeriod.get(key);
                    if (matchedCommission != null) {
                        fortune.setCommissionBizId(matchedCommission.getCommissionBizId());
                    } else {
                        fortune.setStatus(FortuneStatusEnum.MATCH_FAIL.getItemValue());
                        fortune.setRemark("未找到当前预计发佣对应的来佣");
                    }

                    fortune.setCreateTime(now);
                    fortune.setUpdateTime(now);

                    return fortune;
                })
                .collect(Collectors.toList());
    }

    /**
     * 构建保单号+期次的唯一键
     */
    private String buildPolicyPeriodKey(String policyNo, Object period) {
        return (policyNo == null ? "" : policyNo) + "|" + (period == null ? "" : period.toString());
    }

    private void calculateCurrentFortune(Fortune fortune, List<Fortune> policyPaidFortuneList) {
        // 根据转介人分组
        Map<String, List<Fortune>> brokerFortuneMap = policyPaidFortuneList.stream()
                .collect(Collectors.groupingBy(Fortune::getBrokerBizId));
        // 当前转介人已发薪资
        List<Fortune> brokerFortuneList = brokerFortuneMap.getOrDefault(fortune.getBrokerBizId(), new ArrayList<>());
        // 计算当前佣金条目已发薪资
        BigDecimal brokerPaidAmount = brokerFortuneList.stream()
                .filter(item -> StringUtils.equals(item.getFortuneName(), fortune.getFortuneName())
                        && item.getFortunePeriod().equals(fortune.getFortunePeriod()))
                .map(Fortune::getNetAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 计算当前发佣金额，需要扣减已发薪资
        fortune.setAmount(fortune.getAmount().subtract(brokerPaidAmount));
    }

    private void matchCommission(Fortune fortune, List<Commission> commissionList) {
        for (Commission commission : commissionList) {
            if (StringUtils.equals(commission.getPolicyNo(), fortune.getPolicyNo())
                    && commission.getCommissionPeriod().equals(fortune.getFortunePeriod())
                    && commission.getCommissionName().equals(fortune.getFortuneName())
            ) {
                fortune.setCommissionBizId(commission.getCommissionBizId());
            } else {
                fortune.setStatus(FortuneStatusEnum.MATCH_FAIL.getItemValue());
                fortune.setRemark("未找到当前预计发佣对应的来佣");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addCommission(CommissionAddRequest commissionAddRequest) {
        // 校验来佣所属保单是否存在
        String policyNo = commissionAddRequest.getPolicyNo();
        Policy policy = policyService.getOne(new QueryWrapper<Policy>().eq("policy_no", policyNo));
        // 校验来佣所属保单跟进是否存在
        boolean policyFollowExists = policyFollowService.exists(new QueryWrapper<PolicyFollow>().eq("policy_no", policyNo));
        if (policy == null && !policyFollowExists) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "该保单号不存在，不能添加来佣");
        }
        if (ObjectUtils.isEmpty(commissionAddRequest.getPremium())) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "保费不能为空");
        }

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

        Commission commission = new Commission();
        BeanUtils.copyProperties(commissionAddRequest, commission);
        // 生成新单编号
        String commissionBizId = RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_COMMISSION.getCode());
        commission.setCommissionBizId(commissionBizId);
        // 计算当前来佣比例=当前入账金额/结算汇率/保费
        BigDecimal currentCommissionRatio = commissionAddRequest.getAmount()
                .divide(new BigDecimal(commissionAddRequest.getPremium()), 4, RoundingMode.HALF_UP)
                .divide(new BigDecimal(commissionAddRequest.getExchangeRate()), 4, RoundingMode.HALF_UP)
                .multiply(new BigDecimal(100));
        commission.setCurrentCommissionRatio(currentCommissionRatio);

        commission.setCreatorId(loginUserId);
        commission.setCreateTime(new Date());
        commission.setUpdaterId(loginUserId);
        commission.setUpdateTime(new Date());
        // 保存来佣
        this.save(commission);

        // 注册事务同步器，在主事务提交后执行比对
        TransactionSynchronizationManager.registerSynchronization(
                new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {
                        try {
                            // 重新查询最新的数据，获取已提交的数据
                            commissionAsyncService.commissionCompare(commission);
                        } catch (Exception e) {
                            // 比对失败不影响主事务，记录日志即可
                            log.error("主事务提交后，比对操作执行失败，commissionBizId: {}",
                                    commissionBizId, e);
                        }
                    }
                }
        );

        return true;
    }

    @Override
    public Commission getByCommissionBizId(String commissionBizId) {
        return this.getOne(new QueryWrapper<Commission>().eq("commission_biz_id", commissionBizId));
    }

    /**
     * 比对时，当前来佣比例=当前入账金额/结算汇率/保费 和预计来佣的来佣比例对比
     *
     * @param commission
     * @param expected
     */
    @Override
    public void compareWithExpected(String premium, Commission commission, CommissionExpected expected) {
        if (expected != null) {
            if (ObjectUtils.isEmpty(expected.getCommissionRatio())) {
                log.error("预计来佣的来佣比例不能为空，预计来佣业务id：{}", expected.getCommissionExpectedBizId());
                throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "预计来佣的来佣比例不能为空");
            }

            // 当前来佣比例=当前入账金额/结算汇率/保费 * 100
            BigDecimal currentCommissionRatio = commission.getAmount()
                    .divide(new BigDecimal(premium), 4, RoundingMode.HALF_UP)
                    .divide(new BigDecimal(commission.getExchangeRate()), 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal(100))
                    ;
            // 预计来佣比例
            BigDecimal expectedCommissionRatio = expected.getCommissionRatio();

            if (currentCommissionRatio.compareTo(expectedCommissionRatio) == 0) {
                // 一致，来佣状态设置为 比对成功
                commission.setStatus(CommissionStatusEnum.COMPARE_SUCCESS.getItemValue());
                // 预计来佣的状态设置为 已来佣
                expected.setStatus(CommissionExpectedStatusEnum.COMPARED.getItemValue());
            } else {
                // 不一致，来佣状态设置为 比对失败
                commission.setStatus(CommissionStatusEnum.COMPARE_FAIL.getItemValue());
                expected.setStatus(CommissionExpectedStatusEnum.PARTIAL.getItemValue());
            }
            // 设置来佣相关字段
            commission.setCommissionExpectedBizId(expected.getCommissionExpectedBizId());
//            commission.setExpectedAmount(expectedAmount.setScale(2, RoundingMode.HALF_UP));
//            commission.setPaidAmount(paidAmount.setScale(2, RoundingMode.HALF_UP));
//            // 待入账金额 = 预计来佣总金额 - 已入账金额
//            commission.setPendingAmount(expectedAmount.subtract(paidAmount).setScale(2, RoundingMode.HALF_UP));
            // 当前来佣比例
            commission.setCurrentCommissionRatio(currentCommissionRatio);
            // 当期已入账来佣比例 = 当前来佣比例 + 已入账来佣比例
//            commission.setPeriodPaidRatio(currentCommissionRatio.add(ObjectUtils.defaultIfNull(expected.getPaidRatio(), BigDecimal.ZERO)).setScale(2, RoundingMode.HALF_UP));
//            // 当期剩余来佣比例 = 预计来佣来佣比例 - 当期已入账来佣比例
//            commission.setPeriodPendingRatio(expectedCommissionRatio.subtract(commission.getPeriodPaidRatio()).setScale(2, RoundingMode.HALF_UP));

            // 保存比对记录
//            saveCompareRecord(commission, expected);
        }
    }

    public BigDecimal calculateCurrentCommissionRatio(BigDecimal amount, String premium, String exchangeRate) {
        if (ObjectUtils.isEmpty(premium)) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "保费不能为空");
        }
        if (ObjectUtils.isEmpty(amount)) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "当前入账金额不能为空");
        }
        if (ObjectUtils.isEmpty(exchangeRate)) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "当前结算汇率不能为空");
        }
        // 当前来佣比例=当前入账金额/结算汇率/保费 * 100
        return amount.divide(new BigDecimal(premium), 4, RoundingMode.HALF_UP)
                .divide(new BigDecimal(exchangeRate), 4, RoundingMode.HALF_UP)
                .multiply(new BigDecimal(100));
    }

    @Override
    public void saveCompareRecord(Commission commission, CommissionExpected expected, AuthUserDto currentLoginUser) {
        CommissionCompareRecord commissionCompareRecord = new CommissionCompareRecord();
        commissionCompareRecord.setCommissionExpectedBizId(expected.getCommissionExpectedBizId());
        commissionCompareRecord.setCommissionBizId(commission.getCommissionBizId());
        commissionCompareRecord.setCommissionPeriod(commission.getCommissionPeriod());
        commissionCompareRecord.setTotalPeriod(commission.getTotalPeriod());
        commissionCompareRecord.setAmount(commission.getAmount());
        commissionCompareRecord.setCurrentCommissionRatio(commission.getCurrentCommissionRatio());
        commissionCompareRecord.setCurrency(commission.getCurrency());
        commissionCompareRecord.setExchangeRate(commission.getExchangeRate());
        commissionCompareRecord.setStatus(commission.getStatus());
        commissionCompareRecord.setRemark(commission.getRemark());
        commissionCompareRecord.setCreateTime(commission.getCreateTime());
        commissionCompareRecordService.save(commissionCompareRecord);
    }

    @Override
    public CommissionCompareRecord getNewCompareRecord(Commission commission, CommissionExpected expected, AuthUserDto currentLoginUser) {
        CommissionCompareRecord commissionCompareRecord = new CommissionCompareRecord();
        commissionCompareRecord.setCommissionExpectedBizId(expected.getCommissionExpectedBizId());
        commissionCompareRecord.setCommissionBizId(commission.getCommissionBizId());
        commissionCompareRecord.setCommissionPeriod(commission.getCommissionPeriod());
        commissionCompareRecord.setTotalPeriod(commission.getTotalPeriod());
        commissionCompareRecord.setAmount(commission.getAmount());
        commissionCompareRecord.setCurrency(commission.getCurrency());
        commissionCompareRecord.setExchangeRate(commission.getExchangeRate());
        commissionCompareRecord.setStatus(commission.getStatus());
        commissionCompareRecord.setRemark(commission.getRemark());
        commissionCompareRecord.setOperatorName(currentLoginUser.getUsername());
        commissionCompareRecord.setCreateTime(commission.getCreateTime());
        return commissionCompareRecord;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addCommissionBatch(List<CommissionAddRequest> customerAddRequestList) {
        if (CollectionUtils.isEmpty(customerAddRequestList)) {
            throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "来佣数据不能为空");
        }
        Set<String> policyNoSet = customerAddRequestList.stream().map(CommissionAddRequest::getPolicyNo).collect(Collectors.toSet());
        // 校验来佣所属保单是否存在
        List<Policy> policyList = policyService.list(new QueryWrapper<Policy>().in("policy_no", policyNoSet));
        // 校验来佣所属保单跟进是否存在
        if (!CollectionUtils.isEmpty(policyList)) {
            // 保单号映射
            Map<String, Policy> policyMap = policyList.stream().collect(Collectors.toMap(Policy::getPolicyNo, Function.identity()));
            for (CommissionAddRequest commissionAddRequest : customerAddRequestList) {
                Policy policy = policyMap.get(commissionAddRequest.getPolicyNo());
                if (policy == null) {
                    throw new BusinessException(ResultCode.NULL_ERROR.getCode(), String.format("保单号：%s不存在，不能添加来佣", commissionAddRequest.getPolicyNo()));
                }
                if (ObjectUtils.isEmpty(commissionAddRequest.getPremium())) {
                    throw new BusinessException(ResultCode.NULL_ERROR.getCode(), String.format("保单号：%s保费不能为空", commissionAddRequest.getPolicyNo()));
                }
            }
        }

        List<Commission> commissionList = new ArrayList<>();
        for (CommissionAddRequest request : customerAddRequestList) {
            Commission commission = new Commission();
            BeanUtils.copyProperties(request, commission);

            commission.setCommissionBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_COMMISSION.getCode()));
            commissionList.add(commission);
        }
        boolean b = this.saveOrUpdateBatch(commissionList);

        // 开启新事务，比对数据
        transactionTemplate.execute(status -> {
            try {
                commissionAsyncService.commissionCompareBatch(commissionList);
            } catch (Exception e) {
                // 比对失败不影响主事务，记录日志即可
                log.error("批量新增, 比对操作执行失败, error: {}", e.getMessage());
            }
            return null;
        });

        return b;
    }

    @Override
    public Boolean addToExpected(AddToExpectedCommissionRequest addToExpectedCommissionRequest) {
        if (StringUtils.isBlank(addToExpectedCommissionRequest.getCommissionBizId())) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "来佣业务id不能为空");
        }
        Commission commission = this.getByCommissionBizId(addToExpectedCommissionRequest.getCommissionBizId());
        if (commission == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "来佣记录不存在");
        }

        if (commission.getCommissionExpectedBizId() != null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "预计来佣已存在");
        }

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

        CommissionExpected expected = new CommissionExpected();
        BeanUtils.copyProperties(commission, expected);
        expected.setCommissionExpectedBizId(RandomStringGenerator.generateBizId16("commission_expected"));
        // 设置预计入账金额、已来佣金额、待入账金额
        expected.setExpectedAmount(commission.getAmount());
        expected.setPaidAmount(BigDecimal.ZERO);
        expected.setPaidRatio(BigDecimal.valueOf(100));
        expected.setDefaultExchangeRate(new BigDecimal(commission.getExchangeRate()));
        expected.setCommissionDate(new Date());

        expected.setCreatorId(loginUserId);
        expected.setCreateTime(new Date());
        expected.setUpdaterId(loginUserId);
        expected.setUpdateTime(new Date());

        return commissionExpectedService.save(expected);
    }

    /**
     * 查询列表
     *
     * @param dto
     * @return
     */
    @Override
    public List<Commission> queryList(CommissionDto dto) {
        List<Commission> list = baseMapper.selectList(new LambdaQueryWrapper<Commission>()
                .in(CollectionUtils.isNotEmpty(dto.getCommissionBizIdList()), Commission::getCommissionBizId, dto.getCommissionBizIdList())
        );
        return list;
    }

    /**
     * 查询来佣保单转介人关系信息
     *
     * @param dto
     * @return
     */
    @Override
    public List<CommissionBindPolicyBrokerDto> queryCommissionBindPolicyBrokerList(CommissionDto dto) {
        return baseMapper.queryCommissionBindPolicyBrokerList(dto);
    }

    @Override
    public CommissionStatisticsVO getCommissionStatistics(List<Long> commissionIds) {
        // 自定义统计数据
        CommissionStatisticsVO commissionStatistics = baseMapper.getCommissionStatistics(commissionIds);

        BigDecimal totalAmount = commissionStatistics.getTotalAmount();
        BigDecimal totalPaidAmount = commissionStatistics.getTotalPaidAmount();
        // 计算待入账金额
        commissionStatistics.setPendingPaidAmount(totalAmount.subtract(totalPaidAmount));
        // 计算已入账比例
        BigDecimal divided = BigDecimal.ZERO;
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            divided = totalPaidAmount.divide(totalAmount, 4, RoundingMode.HALF_UP);
        }
        // 格式化 %
        commissionStatistics.setPaidAmountRatio(String.format("%.2f%%", divided.doubleValue() * 100));

        return commissionStatistics;
    }
}




