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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.common.constant.RedisConstants;
import com.yd.common.enums.*;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.common.utils.RedisUtil;
import com.yd.csf.api.dto.*;
import com.yd.csf.api.service.ApiBasicLawCalculateService;
import com.yd.csf.api.service.ApiExpectedFortuneLogService;
import com.yd.csf.api.service.ApiExpectedFortuneService;
import com.yd.csf.feign.dto.appointmentfile.ItineraryDto;
import com.yd.csf.feign.request.expectedfortune.*;
import com.yd.csf.feign.response.expectedfortune.ApiExpectedFortunePageResponse;
import com.yd.csf.feign.response.expectedfortune.ApiGenerateExpectedFortuneResponse;
import com.yd.csf.service.dto.CommissionRuleBindingDto;
import com.yd.csf.service.dto.QueryPolicyAndBrokerDto;
import com.yd.csf.service.dto.QueryPolicyBrokerDto;
import com.yd.csf.service.enums.FortuneStatusEnum;
import com.yd.csf.service.model.CommissionRuleBinding;
import com.yd.csf.service.model.ExpectedFortune;
import com.yd.csf.service.model.Policy;
import com.yd.csf.service.model.PolicyBroker;
import com.yd.csf.service.service.ICommissionRuleBindingService;
import com.yd.csf.service.service.IExpectedFortuneService;
import com.yd.csf.service.service.PolicyBrokerService;
import com.yd.csf.service.service.PolicyService;
import com.yd.csf.service.vo.ExpectedFortuneExportDTO;
import com.yd.csf.service.vo.ExpectedFortuneStatisticsVO;
import com.yd.csf.service.vo.PayableReportVO;
import com.yd.user.feign.client.clientuser.ApiClientUserFeignClient;
import com.yd.user.feign.client.sysdict.ApiSysDictFeignClient;
import com.yd.user.feign.response.sysdict.GetDictItemListByDictTypeResponse;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService {
    @Autowired
    private PolicyService policyService;
    @Autowired
    private ApiClientUserFeignClient apiClientUserFeignClient;
    @Autowired
    private ICommissionRuleBindingService iCommissionRuleBindingService;
    @Autowired
    private ApiBasicLawCalculateService apiBasicLawCalculateService;
    @Autowired
    private IExpectedFortuneService iExpectedFortuneService;
    @Autowired
    private ApplicationContext applicationContext;

    private ApiExpectedFortuneService getSelf() {
        return applicationContext.getBean(ApiExpectedFortuneService.class);
    }
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ApiExpectedFortuneLogService apiExpectedFortuneLogService;
    @Autowired
    private PolicyBrokerService policyBrokerService;
    @Autowired
    private ApiSysDictFeignClient apiSysDictFeignClient;

    /**
     * 生成预计发佣
     *
     * @param request
     * @return
     */
    @Override
    public Result<ApiGenerateExpectedFortuneResponse> generate(ApiGenerateExpectedFortuneRequest request) {
        //查询当前保单号是否正在执行预计发佣的缓存，有值就说明正在执行，无值说明执行完毕或者没有执行。
        String value = redisUtil.getCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
        if (StringUtil.isNotBlank(value)) {
            //有值正在执行，提示
            return Result.fail("生成预计发佣正在执行中，无需再次执行");
        } else {
            //无值设置值，表示正在执行
            redisUtil.setCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo(), request.getPolicyNo());
        }
        List<ExpectedFortune> list = iExpectedFortuneService.queryList(request.getPolicyNo());
        if (!CollectionUtils.isEmpty(list)) {
            throw new BusinessException("当前保单已经生成过预计发佣数据，无需再次生成");
        }
        //根据保单号查询保单和转介人列表信息
        List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList = policyService.queryPolicyBrokerList(request.getPolicyNo());
        if (CollectionUtils.isEmpty(queryPolicyAndBrokerDtoList)) {
            throw new BusinessException("保单和转介人信息不存在");
        }

        //查询所有绑定基本法的业务员(客户端用户表用户), 计算业务员绑定的所有基本法对应的计算值, 新增积分明细和发佣记录
        List<CommissionRuleBinding> commissionRuleBindingList = new ArrayList<>();
        //根据租户ID和项目ID查询客户端用户ID列表
        Result<List<String>> result = apiClientUserFeignClient.clientUserBizIdList(TenantEnum.YD.getTenantBizId(), ProjectEnum.CSF_MINI_PROGRAM.getProjectBizId());
        if (!CollectionUtils.isEmpty(result.getData())) {
            commissionRuleBindingList = iCommissionRuleBindingService.queryList(CommissionRuleBindingDto.builder()
                    .targetIdList(result.getData())
                    .build());
        }
        if (CollectionUtils.isEmpty(commissionRuleBindingList)) {
            throw new BusinessException("绑定基本法数据不存在");
        }

        //遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
        // 通过代理对象调用
        getSelf().execute(queryPolicyAndBrokerDtoList, commissionRuleBindingList, request.getPolicyNo());

        return Result.success(null, "生成预计发佣正在处理....，稍后查看预计发佣列表");
    }

    /**
     * 同步生成预计发佣
     *
     * @param request
     * @return
     */
    public Result<ApiGenerateExpectedFortuneResponse> generateSync(ApiGenerateExpectedFortuneRequest request) {
        //查询当前保单号是否正在执行预计发佣的缓存，有值就说明正在执行，无值说明执行完毕或者没有执行。
        String value = redisUtil.getCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
        if (StringUtil.isNotBlank(value)) {
            //有值正在执行，提示
            return Result.fail("生成预计发佣正在执行中，无需再次执行");
        } else {
            //无值设置值，表示正在执行
            redisUtil.setCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo(), request.getPolicyNo());
        }
        List<ExpectedFortune> list = iExpectedFortuneService.queryList(request.getPolicyNo());
        if (!CollectionUtils.isEmpty(list)) {
            throw new BusinessException("当前保单已经生成过预计发佣数据，无需再次生成");
        }
        //根据保单号查询保单和转介人列表信息
        List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList = policyService.queryPolicyBrokerList(request.getPolicyNo());
        if (CollectionUtils.isEmpty(queryPolicyAndBrokerDtoList)) {
            throw new BusinessException("保单和转介人信息不存在");
        }

        //查询所有绑定基本法的业务员(客户端用户表用户), 计算业务员绑定的所有基本法对应的计算值, 新增积分明细和发佣记录
        List<CommissionRuleBinding> commissionRuleBindingList = new ArrayList<>();
        //根据租户ID和项目ID查询客户端用户ID列表
        Result<List<String>> result = apiClientUserFeignClient.clientUserBizIdList(TenantEnum.YD.getTenantBizId(), ProjectEnum.CSF_MINI_PROGRAM.getProjectBizId());
        if (!CollectionUtils.isEmpty(result.getData())) {
            commissionRuleBindingList = iCommissionRuleBindingService.queryList(CommissionRuleBindingDto.builder()
                    .targetIdList(result.getData())
                    .build());
        }
        if (CollectionUtils.isEmpty(commissionRuleBindingList)) {
            throw new BusinessException("绑定基本法数据不存在");
        }

        //遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表（同步执行）
        executeSync(queryPolicyAndBrokerDtoList, commissionRuleBindingList, request.getPolicyNo());

        return Result.success(null, "生成预计发佣成功");
    }

    /**
     * 生成预计发佣（包含日志记录和Redis处理）
     * 用于内部服务调用，会自动保存日志和异常时销毁Redis缓存
     *
     * @param request
     * @return
     */
    @Override
    public Result<ApiGenerateExpectedFortuneResponse> generateWithLogAndRedis(ApiGenerateExpectedFortuneRequest request) {
        try {
            Result<ApiGenerateExpectedFortuneResponse> result = generate(request);
            // 保存预计发佣日志记录（成功）
            apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                    .errorMsg(result.getMsg())
                    .policyNo(request.getPolicyNo())
                    .status(2)
                    .build());
            return result;
        } catch (Exception e) {
            // 抛出异常，销毁redis缓存
            try {
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
            } catch (Exception redisEx) {
                log.error("删除Redis缓存失败: {}", redisEx.getMessage(), redisEx);
            }
            // 保存预计发佣日志记录（失败）
            try {
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(request.getPolicyNo())
                        .status(1)
                        .build());
            } catch (Exception logEx) {
                log.error("保存预计发佣日志失败: {}", logEx.getMessage(), logEx);
            }
            // 重新抛出原始异常，确保上层事务能够回滚
            throw e;
        }
    }

    /**
     * 同步生成预计发佣（包含日志记录和Redis处理）
     * 用于需要同步等待完成的场景
     *
     * @param request
     * @return
     */
    @Override
    public Result<ApiGenerateExpectedFortuneResponse> generateSyncWithLogAndRedis(ApiGenerateExpectedFortuneRequest request) {
        try {
            Result<ApiGenerateExpectedFortuneResponse> result = generateSync(request);
            // 保存预计发佣日志记录（成功）
            apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                    .errorMsg(result.getMsg())
                    .policyNo(request.getPolicyNo())
                    .status(2)
                    .build());
            return result;
        } catch (Exception e) {
            // 抛出异常，销毁redis缓存
            try {
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
            } catch (Exception redisEx) {
                log.error("删除Redis缓存失败: {}", redisEx.getMessage(), redisEx);
            }
            // 保存预计发佣日志记录（失败）
            try {
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(request.getPolicyNo())
                        .status(1)
                        .build());
            } catch (Exception logEx) {
                log.error("保存预计发佣日志失败: {}", logEx.getMessage(), logEx);
            }
            // 重新抛出原始异常，确保上层事务能够回滚
            throw e;
        }
    }

    /**
     * 异步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
     *
     * @param queryPolicyAndBrokerDtoList
     * @param commissionRuleBindingList
     * @return
     */
    @Override
    @Async("commonAsyncExecutor")
    public Result execute(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
                          List<CommissionRuleBinding> commissionRuleBindingList,
                          String policyNo) {

        // 使用编程式事务，确保异步方法内的事务一致性
        return transactionTemplate.execute(status -> {
            try {
                for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
                    Integer paymentTerm = brokerDto.getPaymentTerm();
                    if (Objects.isNull(paymentTerm)) {
                        throw new BusinessException("保单的供款年期不存在");
                    }

                    for (int i = 1; i <= paymentTerm; i++) {
                        executeBilling(ExecuteBillingDto.builder()
                                .name(brokerDto.getBrokerName())
                                .policyAndBrokerDto(brokerDto)
                                .issueNumber(i)
                                .build());

                        for (CommissionRuleBinding binding : commissionRuleBindingList) {
                            executeReward(ExecuteBillingDto.builder()
                                    .clientUserBizId(binding.getTargetId())
                                    .name(binding.getTargetName())
                                    .policyAndBrokerDto(brokerDto)
                                    .issueNumber(i)
                                    .build());
                        }
                    }
                }
                //批量设置应付款编号、发佣类型
                updatePayableNoBatch(policyNo);

                //执行成功完毕，也要销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);

                //保存预计发佣日志记录
                //成功日志
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg("")
                        .policyNo(policyNo)
                        .status(0)
                        .build());
                return Result.success();
            } catch (Exception e) {
                //抛出异常，销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);
                status.setRollbackOnly(); // 标记回滚
                log.error("异步执行预计发佣失败，已回滚所有操作", e);
                //保存预计发佣日志记录
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(policyNo)
                        .status(1)
                        .build());
                throw new BusinessException("异步处理失败: " + e.getMessage());
            }
        });
    }

    /**
     * 同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
     *
     * @param queryPolicyAndBrokerDtoList
     * @param commissionRuleBindingList
     * @param policyNo
     * @return
     */
    public Result executeSync(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
                              List<CommissionRuleBinding> commissionRuleBindingList,
                              String policyNo) {

        // 使用编程式事务，确保方法内的事务一致性
        return transactionTemplate.execute(status -> {
            try {
                for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
                    Integer paymentTerm = brokerDto.getPaymentTerm();
                    if (Objects.isNull(paymentTerm)) {
                        throw new BusinessException("保单的供款年期不存在");
                    }

                    for (int i = 1; i <= paymentTerm; i++) {
                        executeBilling(ExecuteBillingDto.builder()
                                .name(brokerDto.getBrokerName())
                                .policyAndBrokerDto(brokerDto)
                                .issueNumber(i)
                                .build());

                        for (CommissionRuleBinding binding : commissionRuleBindingList) {
                            executeReward(ExecuteBillingDto.builder()
                                    .clientUserBizId(binding.getTargetId())
                                    .name(binding.getTargetName())
                                    .policyAndBrokerDto(brokerDto)
                                    .issueNumber(i)
                                    .build());
                        }
                    }
                }
                //批量设置应付款编号
                updatePayableNoBatch(policyNo);

                //执行成功完毕，也要销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);

                //保存预计发佣日志记录
                //成功日志
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg("")
                        .policyNo(policyNo)
                        .status(0)
                        .build());
                return Result.success();
            } catch (Exception e) {
                //抛出异常，销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);
                status.setRollbackOnly(); // 标记回滚
                log.error("同步执行预计发佣失败，已回滚所有操作", e);
                //保存预计发佣日志记录
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(policyNo)
                        .status(1)
                        .build());
                throw new BusinessException("同步处理失败: " + e.getMessage());
            }
        });
    }

    private void updatePayableNoBatch(String policyNo) {
        // 查询最新一条有 payableNo 记录
        ExpectedFortune latest = iExpectedFortuneService.getOne(
                new QueryWrapper<ExpectedFortune>().isNotNull("payable_no").orderByDesc("id").last("LIMIT 1"),
                true
        );
        //查询当前保单号的所有预计发佣记录
        List<ExpectedFortune> expectedFortuneList = iExpectedFortuneService.queryList(policyNo);

        //查询redis缓存的字典列表信息
        List<GetDictItemListByDictTypeResponse> dictTypeResponses = redisUtil.getCacheObject(RedisConstants.DICT_LIST);

        //获取当前序号作为起点
        int currentSeq = 0;
        if (!Objects.isNull(latest)) {
            currentSeq = Integer.parseInt(latest.getPayableNo().substring(12));
        }

        //批量更新应付款编号，每个item递增
        ExpectedFortune expectedFortune;
        for (int i = 0; i < expectedFortuneList.size(); i++) {
            expectedFortune = expectedFortuneList.get(i);
            expectedFortune.setPayableNo(this.createPayableNo("R", currentSeq + i + 1));
            expectedFortune.setFortuneType(GetDictItemListByDictTypeResponse.getItemValue(dictTypeResponses,
                    "csf_fortune_type", expectedFortune.getFortuneName()));
        }
        iExpectedFortuneService.updateBatchById(expectedFortuneList);
    }

    /**
     * 执行 - 奖励（非销售佣金基本法）
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result executeReward(ExecuteBillingDto dto) {
        QueryPolicyAndBrokerDto brokerDto = dto.getPolicyAndBrokerDto();
        //构造销售佣金基本法项目的顺序下标值执行
        List<Integer> executionOrderList = new ArrayList<>();
        executionOrderList.add(1);

        //执行获得积分用户的非销售佣金基本法项目
        Result<List<AlgorithmResDto>> result = apiBasicLawCalculateService.policyBrokerAlgorithm(AlgorithmDto.builder()
                .brokerBizId(brokerDto.getBrokerBizId())
                .clientUserBizId(dto.getClientUserBizId())
                .sqlTemplateParamDto(SqlTemplateParamDto.builder()
                        .brokerBizId(brokerDto.getBrokerBizId())
                        .clientUserBizId(dto.getClientUserBizId())
                        .policyNo(brokerDto.getPolicyNo())
                        .productBizId(brokerDto.getPlanBizId())
                        .productCode(brokerDto.getProductCode())
                        .term(brokerDto.getPaymentTerm())
                        .issueNumber(dto.getIssueNumber())
                        .build())
                .executionOrderList(executionOrderList)
                .isNegateExecutionOrderList(true)
                .build());

        //生成保单预计发佣表记录 (非销售佣金基本法)
        generateExpectedFortune(GenerateExpectedFortuneDto.builder()
                //获得积分的用户绑定的基本法列表对应计算值
                .algorithmResDtoList(result.getData())
                //保单发佣批次ID
                //获得积分的用户姓名
                .broker(dto.getName())
                //获得积分的用户业务ID
                .brokerBizId(dto.getClientUserBizId())
                //发佣币种
                .currency(brokerDto.getCurrency())
                //发佣期数
                .fortunePeriod(dto.getIssueNumber())
                //发佣总期数
                .fortuneTotalPeriod(brokerDto.getPaymentTerm())
                //发佣类型 TODO
//                .fortuneType()
                //发佣日期 保单生效日期 + 3个月
                .payoutDate(calculateExpectedPayoutDate(brokerDto.getEffectiveDate(), dto.getIssueNumber()))
                //保单号
                .policyNo(brokerDto.getPolicyNo())
                //佣金发放状态 0=待出账 1=可出账 2=已出账
                .status(FortuneStatusEnum.WAIT.getItemValue())
                //期交保费
                .paymentPremium(brokerDto.getPaymentPremium())
                //所属团队 TODO
//                .team()
                //所属团队业务ID TODO
//                .teamBizId()
                .build());

        return Result.success();
    }

    private static LocalDate calculateExpectedPayoutDate(LocalDate coolingOffEndDate, Integer issueNumber) {
        return coolingOffEndDate.plusMonths(2).plusYears(issueNumber - 1);
    }

    /**
     * 执行 - 销售佣金（销售佣金基本法）
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result executeBilling(ExecuteBillingDto dto) {
        QueryPolicyAndBrokerDto brokerDto = dto.getPolicyAndBrokerDto();
        //构造销售佣金基本法项目的顺序下标值执行
        List<Integer> executionOrderList = new ArrayList<>();
        executionOrderList.add(1);

        //保单出账先执行销售佣金基本法项目，入完到积分明细表里后，再执行保单绑定人的其他基本法项目
        Result<List<AlgorithmResDto>> result = apiBasicLawCalculateService.policyBrokerAlgorithm(AlgorithmDto.builder()
                .brokerBizId(brokerDto.getBrokerBizId())
                .clientUserBizId(brokerDto.getBrokerBizId())
                .sqlTemplateParamDto(SqlTemplateParamDto.builder()
                        .brokerBizId(brokerDto.getBrokerBizId())
                        .policyNo(brokerDto.getPolicyNo())
                        .productBizId(brokerDto.getPlanBizId())
                        .productCode(brokerDto.getProductCode())
                        .term(brokerDto.getPaymentTerm())
                        .issueNumber(dto.getIssueNumber())
                        .build())
                .executionOrderList(executionOrderList)
                .isNegateExecutionOrderList(false)
                .build());

        log.info("执行 - 销售佣金（销售佣金基本法）——>brokerDto：{}", JSON.toJSONString(brokerDto));
        log.info("执行 - 销售佣金（销售佣金基本法）——>policyBrokerAlgorithm——>result：{}", JSON.toJSONString(result));
        //生成保单预计发佣表记录 (销售佣金基本法)
        generateExpectedFortune(GenerateExpectedFortuneDto.builder()
                //转介人绑定的基本法列表对应计算值
                .algorithmResDtoList(result.getData())
                //保单发佣批次ID
                //获得积分的转介人（业务员）姓名
                .broker(dto.getName())
                //获得积分的转介人（业务员）业务ID
                .brokerBizId(brokerDto.getBrokerBizId())
                //发佣币种
                .currency(brokerDto.getCurrency())
                //发佣期数
                .fortunePeriod(dto.getIssueNumber())
                //发佣总期数
                .fortuneTotalPeriod(brokerDto.getPaymentTerm())
                //发佣类型 TODO
//                .fortuneType()
                //发佣日期
                .payoutDate(calculateExpectedPayoutDate(brokerDto.getCoolingOffEndDate(), dto.getIssueNumber()))
                //保单号
                .policyNo(brokerDto.getPolicyNo())
                //佣金发放状态 0=待出账 1=可出账 2=已出账
                .status(FortuneStatusEnum.WAIT.getItemValue())
                //期交保费
                .paymentPremium(brokerDto.getPaymentPremium())
                //所属团队 TODO
//                .team()
                //所属团队业务ID TODO
//                .teamBizId()
                .build());

        return Result.success();
    }

    /**
     * 生成保单预计发佣表记录
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result generateExpectedFortune(GenerateExpectedFortuneDto fortuneDto) {
        List<ExpectedFortune> fortuneList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(fortuneDto.getAlgorithmResDtoList())) {
            //获得积分业务员绑定的基本法列表对应计算值结果集合
            for (AlgorithmResDto algorithmResDto : fortuneDto.getAlgorithmResDtoList()) {
                if (Objects.isNull(algorithmResDto.getCalculatedValue())
                        || algorithmResDto.getCalculatedValue().compareTo(BigDecimal.ZERO) == 0) {
                    //判空判0
                    continue;
                }
                PolicyBroker policyBroker = policyBrokerService.queryOne(QueryPolicyBrokerDto.builder()
                        .policyNo(fortuneDto.getPolicyNo())
                        .brokerBizId(fortuneDto.getBrokerBizId())
                        .build());
                ExpectedFortune fortune = new ExpectedFortune();
                //发佣金额 -> 计算值 - sqlAlgorithmResultDtoList集合里面的计算值和值
//                fortune.setAmount(algorithmResDto.getCalculatedValue());
                //保单发佣批次ID
//                fortune.setBatchBizId(fortuneDto.getBatchBizId());
                fortune.setFortuneBizType("R");
                fortune.setPolicyCurrency(fortuneDto.getCurrency());
                fortune.setPremium(fortuneDto.getPaymentPremium());
                //转介人姓名 (获得积分的业务员)
                fortune.setBroker(fortuneDto.getBroker());
                //转介人业务ID (获得积分的业务员)
                fortune.setBrokerBizId(fortuneDto.getBrokerBizId());
                //出账项目名称 -> 基本法项目名称
                fortune.setFortuneName(algorithmResDto.getItemName());
                //保单预计发佣业务id
                fortune.setExpectedFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_EXPECTED_FORTUNE.getCode()));
                //基本法项目配置表唯一业务ID
                fortune.setRuleItemBizId(algorithmResDto.getRuleItemBizId());
                //发佣币种
                fortune.setCurrency(fortuneDto.getCurrency());
                //发佣期数
                fortune.setFortunePeriod(fortuneDto.getFortunePeriod());
                //发佣总期数
                fortune.setFortuneTotalPeriod(fortuneDto.getFortuneTotalPeriod());
                //保单号
                fortune.setPolicyNo(fortuneDto.getPolicyNo());
                //预计发佣日期
                fortune.setPayoutDate(fortuneDto.getPayoutDate());
                //默认结算汇率
                fortune.setDefaultExchangeRate(queryDefaultExchangeRate(fortune.getCurrency()));

                // 标准发佣金额 -> 计算值
                BigDecimal standardAmount = algorithmResDto.getCalculatedValue();
                fortune.setStandardAmount(standardAmount);

                // 转介人介绍费占比
                String brokerRatioStr = !Objects.isNull(policyBroker) ? policyBroker.getBrokerRatio() : "100";
                fortune.setBrokerRatio(brokerRatioStr);

                try {
                    // 将百分比字符串转换为小数
                    BigDecimal ratio = new BigDecimal(brokerRatioStr);
                    // 除以100
                    BigDecimal decimalRatio = ratio.divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);

                    // 计算实际预计发佣金额 = 标准发佣金额 × decimalRatio
                    BigDecimal actualAmount = standardAmount.multiply(decimalRatio);
                    fortune.setAmount(actualAmount);
                    // 计算港币金额
                    fortune.setHkdAmount(actualAmount.multiply(fortune.getDefaultExchangeRate()));

                } catch (NumberFormatException e) {
                    // 如果比例不是有效的数字，使用标准金额
                    fortune.setAmount(standardAmount);
                } catch (Exception e) {
                    // 其他异常处理
                    log.error("计算发佣金额异常: {}", brokerRatioStr, e);
                    fortune.setAmount(standardAmount);
                }

                fortuneList.add(fortune);
            }
        }

        if (!CollectionUtils.isEmpty(fortuneList)) {
            iExpectedFortuneService.saveOrUpdateBatch(fortuneList);
        }

        return Result.success();
    }

    @Override
    public Result<ApiExpectedFortunePageResponseVO> list(ApiExpectedFortunePageRequest request) {
        Page<ExpectedFortune> page = new Page<>(request.getPageNo(), request.getPageSize());

        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);
        IPage<ExpectedFortune> iPage = iExpectedFortuneService.page(page, queryWrapper);
        // 查询统计数据
        List<ExpectedFortune> fortuneList = iExpectedFortuneService.list(queryWrapper);
        ExpectedFortuneStatisticsVO statisticsVO = this.getStatistics(fortuneList.stream().map(ExpectedFortune::getId).collect(Collectors.toList()));

        // 组装返回结果
        ApiExpectedFortunePageResponseVO response = new ApiExpectedFortunePageResponseVO();
        response.setStatisticsVO(statisticsVO);
        response.setPage(iExpectedFortuneService.getVOPage(iPage));
        return Result.success(response);
    }

    @Override
    public Boolean add(List<ExpectedFortuneAddRequest> fortuneAddRequestList) {
        if (CollectionUtils.isEmpty(fortuneAddRequestList)) {
            return true;
        }
        validateAdd(fortuneAddRequestList);

        // 查询保单信息
        Set<String> policyNoSet = fortuneAddRequestList.stream()
                .map(ExpectedFortuneAddRequest::getPolicyNo)
                .filter(StringUtils::isNotBlank).collect(Collectors.toSet());

        Map<String, Policy> policyMap = new HashMap<>();
        if (CollUtil.isNotEmpty(policyNoSet)) {
            List<Policy> policyList = policyService.lambdaQuery().in(Policy::getPolicyNo, policyNoSet).list();
            // 保单映射
            policyMap = policyList.stream().collect(Collectors.toMap(Policy::getPolicyNo, Function.identity()));
        }

        // 查询最新一条有 payableNo 记录
        ExpectedFortune latest = iExpectedFortuneService.getOne(
                new QueryWrapper<ExpectedFortune>().isNotNull("payable_no").orderByDesc("id").last("LIMIT 1"),
                true
        );
        //获取当前序号作为起点
        int currentSeq = 0;
        if (!Objects.isNull(latest)) {
            currentSeq = Integer.parseInt(latest.getPayableNo().substring(12));
        }

        List<ExpectedFortune> fortuneList = new ArrayList<>();
        for (ExpectedFortuneAddRequest expectedFortuneDto : fortuneAddRequestList) {
            ExpectedFortune expectedFortune = new ExpectedFortune();
            BeanUtil.copyProperties(expectedFortuneDto, expectedFortune);

            if ("R".equals(expectedFortuneDto.getFortuneBizType())) {
                Policy policy = policyMap.get(expectedFortuneDto.getPolicyNo());
                if (ObjectUtils.isEmpty(policy)) {
                    throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "保单号为" + expectedFortuneDto.getPolicyNo() + "的保单不存在");
                }

                // 设置关联字段
                expectedFortune.setInsuranceCompanyBizId(policy.getInsuranceCompanyBizId());
                expectedFortune.setProductLaunchBizId(policy.getProductLaunchBizId());
                expectedFortune.setPremium(policy.getPaymentPremium());
                expectedFortune.setPolicyCurrency(policy.getCurrency());
            }

            // 预计发佣业务id
            expectedFortune.setExpectedFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_EXPECTED_FORTUNE.getCode()));
            // 预计发佣类型名称
            expectedFortune.setFortuneName(queryByDict(expectedFortuneDto.getFortuneType()));
            // 应付款编号（序号递增）
            expectedFortune.setPayableNo(this.createPayableNo(expectedFortune.getFortuneBizType(), ++currentSeq));
            // 默认结算汇率
            expectedFortune.setDefaultExchangeRate(queryDefaultExchangeRate(expectedFortune.getCurrency()));
            // 计算港币金额
            expectedFortune.setHkdAmount(expectedFortune.getAmount().multiply(expectedFortune.getDefaultExchangeRate()));
            // 已出帐金额、待出帐金额、已出帐比例、待出帐比例
            expectedFortune.setPaidAmount(BigDecimal.ZERO);
            // 转介人比例默认100%
            expectedFortune.setBrokerRatio("100");
            expectedFortune.setUnpaidAmount(expectedFortune.getAmount());
            expectedFortune.setPaidRatio(BigDecimal.ZERO);
            expectedFortune.setUnpaidRatio(BigDecimal.valueOf(100));
            fortuneList.add(expectedFortune);
        }
        iExpectedFortuneService.saveOrUpdateBatch(fortuneList);
        return true;
    }

    private String queryByDict(String fortuneType) {
        //查询redis缓存的字典列表信息
        List<GetDictItemListByDictTypeResponse> dictTypeResponses = redisUtil.getCacheObject(RedisConstants.DICT_LIST);
        String fortuneName = GetDictItemListByDictTypeResponse.getItemLabel(dictTypeResponses,
                "csf_fortune_type", fortuneType);
        if (ObjectUtils.isNotEmpty(fortuneName)) {
            return fortuneName;
        }
        Result<List<GetDictItemListByDictTypeResponse>> result = apiSysDictFeignClient.getDictItemListByDictType("csf_fortune_type");
        if (ObjectUtils.isNotEmpty(result.getData())) {
            for (GetDictItemListByDictTypeResponse dictItem : result.getData()) {
                if (StringUtils.equalsIgnoreCase(dictItem.getItemValue(), fortuneType)) {
                    return dictItem.getItemLabel();
                }
            }
        }
        return null;
    }

    private BigDecimal queryDefaultExchangeRate(String currency) {
        if ("HKD".equalsIgnoreCase(currency)) {
            return BigDecimal.valueOf(1);
        }
        //查询redis缓存的字典列表信息
        List<GetDictItemListByDictTypeResponse> dictTypeResponses = redisUtil.getCacheObject(RedisConstants.DICT_LIST);
        String defaultExchangeRaye = GetDictItemListByDictTypeResponse.getItemLabel(dictTypeResponses,
                "csf_exchange_rate_hkd", currency);
        if (ObjectUtils.isNotEmpty(defaultExchangeRaye)) {
            return new BigDecimal(defaultExchangeRaye);
        }

        Result<List<GetDictItemListByDictTypeResponse>> result = apiSysDictFeignClient.getDictItemListByDictType("csf_exchange_rate_hkd");
        if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(result.getData())) {
            for (GetDictItemListByDictTypeResponse dictItem : result.getData()) {
                if (StringUtils.equalsIgnoreCase(dictItem.getItemLabel(), currency)) {
                    return new BigDecimal(dictItem.getItemValue());
                }
            }
        }
        return BigDecimal.ONE;
    }

    private void validateAdd(List<ExpectedFortuneAddRequest> fortuneAddRequestList) {
        for (ExpectedFortuneAddRequest expectedFortuneDto : fortuneAddRequestList) {
            if (Objects.isNull(expectedFortuneDto.getAmount())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "预计出账金额不能为空");
            }
            if (Objects.isNull(expectedFortuneDto.getCurrency())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "预计出账币种不能为空");
            }
        }
    }

    @Override
    public void exportExpectedFortune(ApiExpectedFortunePageRequest request, HttpServletResponse response) {
        // 查询数据
        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);
        List<ExpectedFortune> fortuneList = iExpectedFortuneService.list(queryWrapper);
        // 先转换为VO, VO 补充了关联信息（产品计划、产品险种、保险公司、用户）
        List<ApiExpectedFortunePageResponse> expectedFortuneVOList = iExpectedFortuneService.toVOList(fortuneList);
        // 转换为导出DTO
        List<ExpectedFortuneExportDTO> exportDataList = expectedFortuneVOList.stream()
                .map(ExpectedFortuneExportDTO::convertToExportDTO)
                .collect(Collectors.toList());

        // 使用EasyExcel导出
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExpectedFortuneExportDTO.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("应收款数据").build();
            excelWriter.write(exportDataList, writeSheet);
        } catch (Exception e) {
            throw new RuntimeException("导出应收款数据失败", e);
        }
    }

    @Override
    public Result<PayableReportResponse> payableReport(ApiExpectedFortunePageRequest request) {
        // 构建查询条件
        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);

        // 先查询所有符合条件的记录ID（用于统计）
        List<ExpectedFortune> allFortuneList = iExpectedFortuneService.list(queryWrapper);
        List<Long> allFortuneIdList = allFortuneList.stream().map(ExpectedFortune::getId).collect(Collectors.toList());

        // 查询统计数据（基于所有符合条件的记录）
        ExpectedFortuneStatisticsVO statisticsVO = this.getStatistics(allFortuneIdList);

        // 应付款报表分页查询 - 按保单号和期数维度统计
        Page<PayableReportVO> reportPage = new Page<>(request.getPageNo(), request.getPageSize());
        IPage<PayableReportVO> payableReportPage = new Page<>(request.getPageNo(), request.getPageSize());
        if (!CollectionUtils.isEmpty(allFortuneIdList)) {
            payableReportPage = iExpectedFortuneService.payableReportPage(reportPage, allFortuneIdList);
        }

        // 组装返回结果
        PayableReportResponse response = new PayableReportResponse();
        response.setStatisticsVO(statisticsVO);
        response.setPage(payableReportPage);
        return Result.success(response);
    }

    /**
     * 创建应付款编号 应付款类型-CSF-年份后两位-6位数字（不重复）
     *
     * @param fortuneBizType 预计发佣业务类型
     * @param seq            序号
     * @return
     */
    private String createPayableNo(String fortuneBizType, int seq) {
        return String.format("%s%s%s",
                fortuneBizType + "-CSF",
                LocalDate.now().getYear() % 100,
                String.format("%06d", seq));
    }

    /**
     * 分页查询 - 预计发佣
     *
     * @param request
     * @return
     */
    @Override
    public Result<IPage<ApiExpectedFortunePageResponse>> page(ApiExpectedFortunePageRequest request) {
        Page<ExpectedFortune> page = new Page<>(request.getPageNo(), request.getPageSize());

        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);
        IPage<ExpectedFortune> iPage = iExpectedFortuneService.page(page, queryWrapper);

        return Result.success(iPage.convert(ApiExpectedFortuneServiceImpl::transform));
    }

    public static ApiExpectedFortunePageResponse transform(ExpectedFortune expectedFortune) {
        ApiExpectedFortunePageResponse response = new ApiExpectedFortunePageResponse();
        BeanUtil.copyProperties(expectedFortune, response);
        return response;
    }

    @Override
    public QueryWrapper<ExpectedFortune> getQueryWrapper(ApiExpectedFortunePageRequest request) {
        QueryWrapper<ExpectedFortune> queryWrapper = new QueryWrapper<>();
        if (request == null) {
            return queryWrapper;
        }
        // 获取参数
        String payableNo = request.getPayableNo();
        String policyNo = request.getPolicyNo();
        LocalDate payoutDateStart = request.getPayoutDateStart();
        LocalDate payoutDateEnd = request.getPayoutDateEnd();
        List<String> statusList = request.getStatusList();
        Integer fortunePeriod = request.getFortunePeriod();
        String fortuneType = request.getFortuneType();
        String team = request.getTeam();
        List<String> teamBizIdList = request.getTeamBizIdList();
        List<String> insuranceCompanyBizIdList = request.getInsuranceCompanyBizIdList();
        List<String> productLaunchBizIdList = request.getProductLaunchBizIdList();
        List<String> brokerBizIdList = request.getBrokerBizIdList();
        List<String> signerBizIdList = request.getSignerBizIdList();
        String fortuneBizType = request.getFortuneBizType();

        // 按保单号模糊查询
        queryWrapper.eq(ObjectUtils.isNotEmpty(payableNo), "payable_no", payableNo);
        queryWrapper.like(StringUtils.isNotBlank(policyNo), "policy_no", policyNo);
        queryWrapper.gt(ObjectUtils.isNotEmpty(payoutDateStart), "payout_date", payoutDateStart);
        queryWrapper.lt(ObjectUtils.isNotEmpty(payoutDateEnd), "payout_date", payoutDateEnd);
        queryWrapper.in(ObjectUtils.isNotEmpty(statusList), "status", statusList);
        queryWrapper.eq(ObjectUtils.isNotEmpty(fortunePeriod), "fortune_period", fortunePeriod);
        queryWrapper.eq(StringUtils.isNotBlank(fortuneType), "fortune_type", fortuneType);
        queryWrapper.like(StringUtils.isNotBlank(team), "team", team);
        queryWrapper.in(ObjectUtils.isNotEmpty(teamBizIdList), "team_biz_id", teamBizIdList);
        queryWrapper.in(ObjectUtils.isNotEmpty(insuranceCompanyBizIdList), "insurance_company_biz_id", insuranceCompanyBizIdList);
        queryWrapper.in(ObjectUtils.isNotEmpty(productLaunchBizIdList), "product_launch_biz_id", productLaunchBizIdList);
        queryWrapper.in(ObjectUtils.isNotEmpty(brokerBizIdList), "broker_biz_id", brokerBizIdList);
        queryWrapper.in(ObjectUtils.isNotEmpty(signerBizIdList), "signer_biz_id", signerBizIdList);
        queryWrapper.eq(StringUtils.isNotBlank(fortuneBizType), "fortune_biz_type", fortuneBizType);

        // 按id升序排序
        queryWrapper.orderByAsc("id");
        return queryWrapper;
    }

    /**
     * 查询保单是否生成过预计发佣
     *
     * @param policyNo
     * @return
     */
    @Override
    public Result<Boolean> isGenerate(String policyNo) {
        List<ExpectedFortune> list = iExpectedFortuneService.queryList(policyNo);
        if (!CollectionUtils.isEmpty(list)) {
            throw new BusinessException("当前保单已经生成过预计发佣数据，无需再次生成");
        }
        return Result.success();
    }

    /**
     * 根据保单号批量删除预计发佣数据
     *
     * @param policyNo
     * @return
     */
    @Override
    public Result batchDelByPolicyNo(String policyNo) {
        iExpectedFortuneService.batchDelByPolicyNo(policyNo);
        return Result.success();
    }

    @Override
    public ExpectedFortuneStatisticsVO getStatistics(List<Long> expectedFortuneIds) {
        if (CollectionUtils.isEmpty(expectedFortuneIds)) {
            ExpectedFortuneStatisticsVO statisticsVO = new ExpectedFortuneStatisticsVO();
            statisticsVO.setTotalExpectedAmount(BigDecimal.ZERO);
            statisticsVO.setTotalPaidAmount(BigDecimal.ZERO);
            statisticsVO.setTotalUnpaidAmount(BigDecimal.ZERO);
            statisticsVO.setPaidAmountRatio(BigDecimal.ZERO);
            statisticsVO.setTotalPolicyCount(0);
            return statisticsVO;
        }
        // 自定义统计数据
        ExpectedFortuneStatisticsVO statisticsVO = iExpectedFortuneService.getStatistics(expectedFortuneIds);

        BigDecimal totalAmount = statisticsVO.getTotalExpectedAmount();
        BigDecimal totalPaidAmount = statisticsVO.getTotalPaidAmount();
        // 计算待出账金额
        statisticsVO.setTotalUnpaidAmount(totalAmount.subtract(totalPaidAmount));
        // 计算已出账比例（已出账金额/预计发佣金额）
        BigDecimal divided = BigDecimal.ZERO;
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            divided = totalPaidAmount.divide(totalAmount, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
        }
        statisticsVO.setPaidAmountRatio(divided);

        return statisticsVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(ExpectedFortuneUpdateRequest request) {
        String expectedFortuneBizId = request.getExpectedFortuneBizId();
        ExpectedFortune expectedFortune = iExpectedFortuneService.getOne(new QueryWrapper<ExpectedFortune>().eq("expected_fortune_biz_id", expectedFortuneBizId));
        if (expectedFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "预计发佣数据不存在");
        }
        if (expectedFortune.getPaidAmount().compareTo(BigDecimal.ZERO) > 0) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "当前应付款已有出账金额，不能修改");
        }

        ExpectedFortune updateExpectedFortune = new ExpectedFortune();
        BeanUtils.copyProperties(request, updateExpectedFortune);
        updateExpectedFortune.setId(expectedFortune.getId());
        // 默认结算汇率
        if (updateExpectedFortune.getDefaultExchangeRate() == null) {
            updateExpectedFortune.setDefaultExchangeRate(queryDefaultExchangeRate(updateExpectedFortune.getCurrency()));
        }
        // 计算港币金额
        updateExpectedFortune.setHkdAmount(updateExpectedFortune.getAmount().multiply(updateExpectedFortune.getDefaultExchangeRate()));

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

        updateExpectedFortune.setUpdaterId(loginUserId);
        updateExpectedFortune.setUpdateTime(LocalDateTime.now());
        iExpectedFortuneService.updateById(updateExpectedFortune);

        return true;
    }
}
