package com.yd.csf.api.controller;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.enums.CommonEnum;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.csf.api.dto.CommissionExcelDTO;
import com.yd.csf.api.dto.PolicyExcelDTO;
import com.yd.csf.api.dto.PolicyFollowDTO;
import com.yd.csf.api.listener.PolicyDataListener;
import com.yd.csf.api.service.ApiExpectedFortuneService;
import com.yd.csf.feign.request.expectedfortune.ApiGenerateExpectedFortuneRequest;
import com.yd.csf.feign.response.expectedfortune.ApiGenerateExpectedFortuneResponse;
import com.yd.csf.service.common.ErrorCode;
import com.yd.csf.service.component.PolicyReportPdfService;
import com.yd.csf.service.dto.*;
import com.yd.csf.service.enums.PolicyFollowStatusEnum;
import com.yd.csf.service.model.*;
import com.yd.csf.service.service.*;
import com.yd.csf.service.vo.PolicyFollowDetailVO;
import com.yd.csf.service.vo.PolicyFollowRecordVO;
import com.yd.csf.service.vo.PolicyFollowVO;
import com.yd.csf.service.vo.PolicyNumberResponseVO;
import com.yd.feign.config.FeignTokenInterceptor;
import com.yd.insurance.base.feign.client.insurancereconciliationcompany.ApiInsuranceReconciliationCompanyFeignClient;
import com.yd.insurance.base.feign.request.insurancereconciliationcompany.ApiInsuranceReconciliationCompanyPageRequest;
import com.yd.product.feign.response.expectedspecies.ApiExpectedSpeciesListResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 新单跟进接口
 *
 * @author jianan
 * @since 2025-07-31
 */
@RestController
@RequestMapping("/policy_follow")
@Tag(name = "新单跟进接口")
public class ApiPolicyFollowController {

    private static final Logger log = LoggerFactory.getLogger(ApiPolicyFollowController.class);

    @Resource
    private PolicyFollowService policyFollowService;
    @Resource
    private PolicyBrokerService policyBrokerService;
    @Resource
    private CustomerService customerService;
    @Resource
    private PolicyFollowRecordService policyFollowRecordService;
    @Resource
    private PolicyFollowFileService policyFollowFileService;
    @Resource
    private PolicyReportPdfService policyReportPdfService;
    @Resource
    private ApiInsuranceReconciliationCompanyFeignClient apiInsuranceReconciliationCompanyFeignClient;
    @Resource
    private ApiExpectedFortuneService apiExpectedFortuneService;
    @Resource
    private CommissionExpectedService commissionExpectedService;


    @PostMapping("/upload/excel")
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> uploadExcel(@RequestParam("file") MultipartFile file) throws Exception {
        // 创建监听器用于读取保单数据
        PolicyDataListener policyDataListener = new PolicyDataListener();

        // 一次性读取所有Sheet
        ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build();

        // 1. 读取保单数据
        ReadSheet readSheet1 = EasyExcel.readSheet("保单")
                .head(PolicyExcelDTO.class)
                .registerReadListener(policyDataListener)
                .build();
        excelReader.read(readSheet1);

        // 2. 读取预计来佣数据
        List<CommissionExcelDTO> dataList = new ArrayList<>();
        ReadSheet readSheet2 = EasyExcel.readSheet("预计来佣")
                .head(CommissionExcelDTO.class)
                .registerReadListener(new AnalysisEventListener<CommissionExcelDTO>() {
                    @Override
                    public void invoke(CommissionExcelDTO data, AnalysisContext context) {
                        dataList.add(data);
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        // 读取完成
                    }
                })
                .build();
        excelReader.read(readSheet2);

        // 关闭读取器
        excelReader.finish();

        List<PolicyExcelDTO> dtoList = policyDataListener.getList();

        // 校验保单号是否重复
        String validateMsg = validatePolicyNo(dtoList);
        if (StringUtils.isNotBlank(validateMsg)) return Result.fail(ErrorCode.OPERATION_ERROR.getCode(), validateMsg);

        // 校验必填字段是否为空
        validateMsg = validateField(dtoList);
        if (StringUtils.isNotBlank(validateMsg)) return Result.fail(ErrorCode.OPERATION_ERROR.getCode(), validateMsg);

        // 查询对账公司
        Set<String> reconciliationCompanyNames = dtoList.stream()
                .map(PolicyExcelDTO::getReconciliationCompany)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());

        // 通过 Feign Client 获取对账公司数据
        List<ReconciliationCompany> reconciliationCompanyList = getReconciliationCompaniesByNames(reconciliationCompanyNames);
        // 关联对账公司
        Map<String, String> reconciliationCompanyMap = reconciliationCompanyList.stream()
                .collect(Collectors.toMap(ReconciliationCompany::getCompanyName, ReconciliationCompany::getReconciliationCompanyBizId, (v1, v2) -> v1));

        // 转换为PolicyFollow对象
        List<PolicyFollowDTO> list = convertToObj(dtoList, reconciliationCompanyMap);

        List<PolicyFollow> policyFollowList = new ArrayList<>();
        List<PolicyBroker> policyBrokerList = new ArrayList<>();
        for (PolicyFollowDTO policyFollow : list) {
            policyFollowList.add(policyFollow.getPolicyFollow());
            policyBrokerList.addAll(policyFollow.getBrokerList());
        }

        policyFollowService.saveBatch(policyFollowList);
        policyBrokerService.saveBatch(policyBrokerList);

        return Result.success(true);
    }

    private String validatePolicyNo(List<PolicyExcelDTO> dtoList) {
        String validateMsg = "";
        // 校验保单号是否为空
        if (dtoList.isEmpty()) {
            validateMsg = "保单数据不能为空";
        }

        Set<String> policyNos = dtoList.stream()
                .map(PolicyExcelDTO::getPolicyNo)
                .collect(Collectors.toSet());
        List<PolicyFollow> existingPolicies = policyFollowService.lambdaQuery()
                .select(PolicyFollow::getPolicyNo)
                .in(PolicyFollow::getPolicyNo, policyNos)
                .list();
        Set<String> existingPolicyNos = existingPolicies.stream()
                .map(PolicyFollow::getPolicyNo)
                .collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(existingPolicyNos)) {
            validateMsg = "保单号 " + String.join(", ", existingPolicyNos) + " 已存在，请检查后重新上传!";
        }
        return validateMsg;
    }

    private String validateField(List<PolicyExcelDTO> dataList) {
        StringBuilder validateMsg = new StringBuilder();
        for (int i = 0; i < dataList.size(); i++) {
            PolicyExcelDTO excelDTO = dataList.get(i);
            int rowNum = i + 1;

            if (StringUtils.isBlank(excelDTO.getPolicyNo())) {
                validateMsg.append("第").append(rowNum).append("行保单号为空\n");
            }
            if (StringUtils.isBlank(excelDTO.getReconciliationCompany())) {
                validateMsg.append("第").append(rowNum).append("行对账公司为空\n");
            }
            if (StringUtils.isBlank(excelDTO.getProductName())) {
                validateMsg.append("第").append(rowNum).append("行产品名称为空\n");
            }
            if (StringUtils.isBlank(excelDTO.getInsuranceCompany())) {
                validateMsg.append("第").append(rowNum).append("行保险公司为空\n");
            }
            if (StringUtils.isBlank(excelDTO.getPolicyHolder())) {
                validateMsg.append("第").append(rowNum).append("行保单持有人为空\n");
            }
            if (StringUtils.isBlank(excelDTO.getInsured())) {
                validateMsg.append("第").append(rowNum).append("行受保人为空\n");
            }
            if (StringUtils.isBlank(excelDTO.getInitialPremium())) {
                validateMsg.append("第").append(rowNum).append("行首期保费为空\n");
            }
        }
        return validateMsg.toString();
    }

    private List<PolicyFollowDTO> convertToObj(List<PolicyExcelDTO> list, Map<String, String> reconciliationCompanyMap) throws ParseException {
        List<PolicyFollowDTO> policyFollowDTOList = new ArrayList<>();

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

        // 获取所有保单持有人
        Set<String> customerNames = list.stream()
                .map(PolicyExcelDTO::getPolicyHolder)
                .collect(Collectors.toSet());
        // 根据保单持有人名称查询用户业务ID
        List<Customer> customerList = customerService.lambdaQuery()
                .in(Customer::getNamePyEn, customerNames)
                .list();
        // 构建保单持有人名称到用户业务ID的映射
        Map<String, String> customerNameToBizIdMap = customerList.stream()
                .collect(Collectors.toMap(Customer::getNamePyEn, Customer::getCustomerBizId, (oldValue, newValue) -> oldValue));

        for (PolicyExcelDTO policyExcelDTO : list) {
            String policyNo = policyExcelDTO.getPolicyNo();
            // 生成新单编号
            String policyBizId = RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_POLICY.getCode());
            // 转换为PolicyFollow对象
            PolicyFollow policyFollow = new PolicyFollow();
            BeanUtils.copyProperties(policyExcelDTO, policyFollow);
            policyFollow.setPolicyBizId(policyBizId);
            policyFollow.setCreatorId(loginUserId);
            policyFollow.setCreateTime(new Date());
            policyFollow.setUpdaterId(loginUserId);
            policyFollow.setUpdateTime(new Date());
            if (StringUtils.isNotBlank(policyExcelDTO.getInitialPremium())) {
                policyFollow.setInitialPremium(new BigDecimal(policyExcelDTO.getInitialPremium()));
            }
            policyFollow.setCustomerName(policyExcelDTO.getPolicyHolder());
            policyFollow.setCustomerBizId(customerNameToBizIdMap.get(policyExcelDTO.getPolicyHolder()));
            PolicyFollowStatusEnum policyFollowStatusEnum = PolicyFollowStatusEnum.getEnumByItemLabel(policyExcelDTO.getStatus());
            if (policyFollowStatusEnum == null) {
                policyFollow.setStatus(null);
            } else {
                policyFollow.setStatus(policyFollowStatusEnum.getItemValue());
            }
            policyFollow.setReconciliationCompanyBizId(reconciliationCompanyMap.get(policyExcelDTO.getReconciliationCompany()));
            if (policyFollow.getEffectiveDate() != null && policyFollow.getCoolingOffDays() != null) {
                policyFollow.setCoolingOffEndDate(DateUtil.offset(policyFollow.getEffectiveDate(), DateField.DAY_OF_MONTH, policyFollow.getCoolingOffDays()));
            }
            // 获取币种字典值
            policyFollow.setPolicyCurrency(policyFollowService.getCurrencyValue(policyExcelDTO.getCurrency()));

            // 转换为Policy对象
            Policy policy = new Policy();
            BeanUtils.copyProperties(policyExcelDTO, policy);
            policy.setPolicyBizId(policyBizId);
            policy.setReconciliationCompanyBizId(policyFollow.getReconciliationCompanyBizId());
            policy.setCoolingOffEndDate(policyFollow.getCoolingOffEndDate());

            // 转换为PolicyBroker对象
            List<PolicyBroker> policyBrokerList = buildPolicyBrokers(policyExcelDTO, policyBizId, policyNo);

            // 转换为PolicyFollowDTO对象
            PolicyFollowDTO policyFollowDTO = new PolicyFollowDTO();
            policyFollowDTO.setPolicyFollow(policyFollow);
            policyFollowDTO.setPolicy(policy);
            policyFollowDTO.setBrokerList(policyBrokerList);
            policyFollowDTOList.add(policyFollowDTO);
        }
        return policyFollowDTOList;
    }

    private PolicyBroker getPolicyBroker(String brokerName, String team, String brokerRatio, String policyBizId, String policyNo) {
        PolicyBroker policyBroker = new PolicyBroker();
        policyBroker.setPolicyBizId(policyBizId);
        policyBroker.setPolicyNo(policyNo);
        policyBroker.setBrokerName(brokerName);
        policyBroker.setTeam(team);
        policyBroker.setBrokerRatio(brokerRatio);
        return policyBroker;
    }

    private List<PolicyBroker> buildPolicyBrokers(PolicyExcelDTO excelDTO, String policyBizId, String policyNo) {
        List<PolicyBroker> brokers = new ArrayList<>();
        addBrokerIfPresent(brokers, excelDTO.getBrokerName1(), excelDTO.getTeam1(), excelDTO.getBrokerRatio1(), policyBizId, policyNo);
        addBrokerIfPresent(brokers, excelDTO.getBrokerName2(), excelDTO.getTeam2(), excelDTO.getBrokerRatio2(), policyBizId, policyNo);
        addBrokerIfPresent(brokers, excelDTO.getBrokerName3(), excelDTO.getTeam3(), excelDTO.getBrokerRatio3(), policyBizId, policyNo);
        addBrokerIfPresent(brokers, excelDTO.getBrokerName4(), excelDTO.getTeam4(), excelDTO.getBrokerRatio4(), policyBizId, policyNo);
        addBrokerIfPresent(brokers, excelDTO.getBrokerName5(), excelDTO.getTeam5(), excelDTO.getBrokerRatio5(), policyBizId, policyNo);
        return brokers;
    }

    private void addBrokerIfPresent(List<PolicyBroker> brokers, String name, String team, String ratio, String policyBizId, String policyNo) {
        if (StringUtils.isNotBlank(name)) {
            brokers.add(getPolicyBroker(name, team, ratio, policyBizId, policyNo));
        }
    }

    /**
     * 创建新单跟进
     *
     * @param policyFollowDto
     * @return
     */
    @Operation(summary = "创建新单跟进（统一DTO）")
    @PostMapping("/add")
    public Result<Map<String, Object>> addPolicyFollowV2(@RequestBody PolicyFollowDto policyFollowDto) {
        if (policyFollowDto == null) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "请求参数不能为空");
        }
        return Result.success(policyFollowService.addPolicyFollowDto(policyFollowDto));
    }

    /**
     * 保存到保单库
     *
     * @param addToPolicyRequest
     * @param request
     * @return
     */
    @Operation(summary = "保存到保单库")
    @PostMapping("/addToPolicy")
    public Result<Boolean> addToPolicy(@RequestBody AddToPolicyRequest addToPolicyRequest, HttpServletRequest request) {
        if (CollectionUtils.isEmpty(addToPolicyRequest.getPolicyNoList())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "保单号列表不能为空");
        }
        return Result.success(policyFollowService.addToPolicy(addToPolicyRequest.getPolicyNoList()));
    }

    /**
     * 删除新单跟进
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @Operation(summary = "删除新单跟进")
    @PostMapping("/delete")
    public Result<Boolean> deletePolicyFollow(@RequestBody PolicyFollowDeleteRequest deleteRequest, HttpServletRequest request) {
        if (StringUtils.isBlank(deleteRequest.getPolicyBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMessage());
        }
        String policyBizId = deleteRequest.getPolicyBizId();

        // 判断是否存在
        PolicyFollow oldFna = policyFollowService.getByPolicyBizId(policyBizId);
        if (oldFna == null) {
            return Result.fail(ErrorCode.NOT_FOUND_ERROR.getCode(), ErrorCode.NOT_FOUND_ERROR.getMessage());
        }

        // 操作数据库
        boolean result = policyFollowService.removeById(oldFna.getId());
        if (!result) {
            return Result.fail(ErrorCode.OPERATION_ERROR.getCode(), ErrorCode.OPERATION_ERROR.getMessage());
        }
        return Result.success(result);
    }

    /**
     * 更新新单跟进（使用统一DTO）
     *
     * @param policyFollowDto
     * @return
     */
    @PostMapping("/update")
    @Operation(summary = "更新新单跟进信息（统一DTO）")
    public Result<Boolean> updatePolicyFollowV2(@RequestBody PolicyFollowDto policyFollowDto) {
        if (policyFollowDto == null || StringUtils.isBlank(policyFollowDto.getPolicyBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
        }
        return Result.success(policyFollowService.updatePolicyFollowDto(policyFollowDto));
    }

    /**
     * 新单跟进上传附件
     *
     * @param attachmentUploadRequest
     * @return
     */
    @PostMapping("/attachment/upload")
    @Operation(summary = "新单跟进上传附件")
    public Result<Boolean> attachmentUpload(@RequestBody AttachmentUploadRequest attachmentUploadRequest) {
        if (StringUtils.isBlank(attachmentUploadRequest.getPolicyBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
        }
        return Result.success(policyFollowService.uploadAttachment(attachmentUploadRequest));
    }

    /**
     * 新单跟进附件列表分页查询
     *
     * @param queryRequest
     * @return
     */
    @PostMapping("/attachment/list/page")
    @Operation(summary = "新单跟进附件列表分页查询")
    public Result<Page<PolicyFollowFile>> attachmentListPage(@RequestBody PolicyFollowFileQueryRequest queryRequest) {
        if (StringUtils.isBlank(queryRequest.getPolicyBizId())) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
        }
        long current = queryRequest.getPageNo();
        long size = queryRequest.getPageSize();

        QueryWrapper<PolicyFollowFile> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("policy_biz_id", queryRequest.getPolicyBizId());
        queryWrapper.eq("is_deleted", 0);
        Page<PolicyFollowFile> policyFollowFilePage = policyFollowFileService.page(new Page<>(current, size), queryWrapper);
        return Result.success(policyFollowFilePage);
    }

    /**
     * 删除新单跟进附件
     *
     * @param fileId
     * @return
     */
    @DeleteMapping("/attachment/delete")
    @Operation(summary = "删除新单跟进附件")
    public Result<Boolean> deletePolicyFollowAttachment(@RequestParam("fileId") Long fileId) {
        if (fileId == null) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "fileId不能为空");
        }
        return Result.success(policyFollowFileService.removeById(fileId));
    }

    /**
     * 根据 policyBizId 获取新单跟进（封装类）
     *
     * @param policyBizId
     * @return
     */
    @GetMapping("/get/vo")
    @Operation(summary = "根据 policyBizId 获取新单跟进详情")
    public Result<PolicyFollowDetailVO> getPolicyFollowByPolicyBizId(@RequestParam("policyBizId") String policyBizId, HttpServletRequest request) {
        if (StringUtils.isBlank(policyBizId)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
        }
        // 查询数据库并组装详情数据（包含附加险和介绍人）
        PolicyFollowDetailVO detailVO = policyFollowService.getPolicyFollowDetailVO(policyBizId);
        if (detailVO == null) {
            return Result.fail(ErrorCode.NOT_FOUND_ERROR.getCode(), ErrorCode.NOT_FOUND_ERROR.getMessage());
        }
        // 获取封装类
        return Result.success(detailVO);
    }

    /**
     * 分页获取新单跟进列表
     *
     * @param policyFollowQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo")
    @Operation(summary = "分页获取新单跟进列表")
    public Result<Page<PolicyFollowVO>> listPolicyFollowByPage(@RequestBody PolicyFollowQueryRequest policyFollowQueryRequest,
                                                                     HttpServletRequest request) {
        long current = policyFollowQueryRequest.getPageNo();
        long size = policyFollowQueryRequest.getPageSize();

        // 查询数据库
        Page<PolicyFollow> policyFollowPage = policyFollowService.page(new Page<>(current, size),
                policyFollowService.getQueryWrapper(policyFollowQueryRequest));
        return Result.success(policyFollowService.getPolicyFollowVOPage(policyFollowPage));
    }

    /**
     * 修改跟进状态
     *
     * @param changePolicyFollowStatusRequest
     * @param request
     * @return
     */
    @PostMapping("/change_status")
    @Operation(summary = "修改跟进状态")
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> changePolicyFollowStatus(@RequestBody ChangePolicyFollowStatusRequest changePolicyFollowStatusRequest,
                                                    HttpServletRequest request) {
        if (changePolicyFollowStatusRequest == null || StringUtils.isBlank(changePolicyFollowStatusRequest.getPolicyBizId())) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
        }

        String policyBizId = changePolicyFollowStatusRequest.getPolicyBizId();
        PolicyFollow policyFollow = policyFollowService.getByPolicyBizId(policyBizId);
        if (policyFollow == null) {
            return Result.fail(ResultCode.NULL_ERROR.getCode(), ResultCode.NULL_ERROR.getMessage());
        }

        PolicyFollowStatusEnum currentStatusEnum = PolicyFollowStatusEnum.getEnumByValue(changePolicyFollowStatusRequest.getStatus());
        // 修改为生效时需要校验、同步预计发佣
        if (PolicyFollowStatusEnum.EFFECTIVE.equals(currentStatusEnum)) {
            if (ObjectUtils.isEmpty(changePolicyFollowStatusRequest.getEffectiveDate())) {
                return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "修改为生效状态时，生效日期不能为空");
            }
            if (ObjectUtils.isEmpty(changePolicyFollowStatusRequest.getUnderwritingDate())) {
                return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "修改为生效状态时，核保日期不能为空");
            }
            String productLaunchBizId = policyFollow.getProductLaunchBizId();
            // 校验产品是否维护预计来佣数据
            if (ObjectUtils.isEmpty(productLaunchBizId)) {
                return Result.fail(ResultCode.NULL_ERROR.getCode(), "新单跟进记录中，productLaunchBizId不能为空");
            }
            List<ApiExpectedSpeciesListResponse> expectedSpeciesList = commissionExpectedService.queryExpectedSpeciesByFeign(productLaunchBizId);
            if (ObjectUtils.isEmpty(expectedSpeciesList)) {
                return Result.fail(ResultCode.NULL_ERROR.getCode(), "产品未维护预计来佣数据");
            }
            // 校验产品是否维护发佣信息
            Boolean hasCommissionInfo = commissionExpectedService.checkProductExpectedFortune(productLaunchBizId);
            if (!hasCommissionInfo) {
                return Result.fail(ResultCode.NULL_ERROR.getCode(), "产品未维护发佣信息");
            }
        }

        // 修改逻辑
        policyFollowService.changePolicyFollowStatus(changePolicyFollowStatusRequest, policyFollow);

        // 修改为生效时需要同步预计发佣
        if (PolicyFollowStatusEnum.EFFECTIVE.equals(currentStatusEnum)) {
            // 获取Token
            String token = request.getHeader("Authorization");
            log.info("修改跟进状态获取token：{}", token);
            // 同步：根据保单生成预计出账记录
            generateExpectedFortuneSync(token, policyFollow.getPolicyNo());
        }
        return Result.success(true);
    }

    /**
     * 新单跟进状态列表查询
     *
     * @param policyBizId
     * @return
     */
    @GetMapping("/status/list")
    @Operation(summary = "新单跟进状态列表查询")
    public Result<List<PolicyFollowRecordVO>> getPolicyFollowRecordList(@RequestParam("policyBizId") String policyBizId) {
        if (policyBizId == null) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
        }
        QueryWrapper<PolicyFollowRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("policy_biz_id", policyBizId);
        queryWrapper.orderByDesc("id");
        List<PolicyFollowRecord> policyFollowStatusList = policyFollowRecordService.list(queryWrapper);
        return Result.success(policyFollowRecordService.getVOList(policyFollowStatusList));
    }

    /**
     * 生成签约单
     */
    @GetMapping(value = "/report/download", produces = "application/pdf")
    @Operation(summary = "生成签约单")
    public void previewPolicyReport(@RequestParam("policyBizId") String policyBizId,
                                    HttpServletResponse response) {

        PolicyReportData reportData = policyFollowService.queryPolicyReportData(policyBizId);
        if (reportData == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR.getCode(), ErrorCode.NOT_FOUND_ERROR.getMessage());
        }

        try {
            log.info("收到PDF生成请求: {}", reportData);

            log.info("PolicyReportData 内容检查:");
            log.info("startTime: {}", reportData.getStartTime());
            log.info("endTime: {}", reportData.getEndTime());
            log.info("location: {}", reportData.getLocation());
            log.info("serviceManager: {}", reportData.getServiceManager());

            byte[] pdfBytes = policyReportPdfService.generatePolicyReport(reportData);

            if (pdfBytes == null || pdfBytes.length == 0) {
                log.error("生成的PDF为空");
                throw new BusinessException("生成的PDF内容为空");
            }

            log.info("PDF生成成功，大小: {} bytes", pdfBytes.length);

            // 设置下载响应头
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "attachment; filename=policy-report-" +
                    reportData.getPolicyNumber() + ".pdf");
            response.setContentLength(pdfBytes.length);

            // 写入响应流
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(pdfBytes);
            outputStream.flush();

            log.info("PDF响应发送完成");

        } catch (Exception e) {
            log.error("PDF预览失败", e);
            throw new BusinessException("PDF预览失败: " + e.getMessage());
        }
    }

    @PostMapping("/save_initial_payment")
    @Operation(summary = "保存首期缴费信息")
    public Result<Boolean> saveInitialPayment(@RequestBody InitialPaymentSaveRequest initialPaymentSaveRequest) {
        log.info("保存首期缴费信息, policyBizId: {}", initialPaymentSaveRequest.getPolicyBizId());
        Boolean result = policyFollowService.saveInitialPayment(initialPaymentSaveRequest);
        return Result.success(result);
    }

    @PostMapping("/save_mailing_info")
    @Operation(summary = "保存邮寄信息")
    public Result<Boolean> saveMailingInfo(@RequestBody MailingInfoSaveRequest mailingInfoSaveRequest) {
        log.info("保存邮寄信息, policyBizId: {}", mailingInfoSaveRequest.getPolicyBizId());
        Boolean result = policyFollowService.saveMailingInfo(mailingInfoSaveRequest);
        return Result.success(result);
    }

    @PostMapping("/batch_save_brokers")
    @Operation(summary = "批量保存介绍人信息")
    public Result<Boolean> batchSaveBrokers(@RequestBody BrokerBatchSaveRequest brokerBatchSaveRequest) {
        log.info("批量保存介绍人信息, policyBizId: {}", brokerBatchSaveRequest.getPolicyBizId());
        Boolean result = policyFollowService.batchSaveBrokers(brokerBatchSaveRequest);
        return Result.success(result);
    }

    /**
     * 通过 Feign Client 根据对账公司名称列表获取对账公司数据
     *
     * @param reconciliationCompanyNames 对账公司名称集合
     * @return 对账公司列表
     */
    @SuppressWarnings("unchecked")
    private List<ReconciliationCompany> getReconciliationCompaniesByNames(Set<String> reconciliationCompanyNames) {
        if (CollectionUtils.isEmpty(reconciliationCompanyNames)) {
            return new ArrayList<>();
        }

        try {
            ApiInsuranceReconciliationCompanyPageRequest request = new ApiInsuranceReconciliationCompanyPageRequest();
            request.setPageNo(1);
            request.setPageSize(100);

            Result<Page<ReconciliationCompany>> result = (Result<Page<ReconciliationCompany>>)
                    (Result<?>) apiInsuranceReconciliationCompanyFeignClient.page(request);

            if (result != null && result.getCode() == 200 && result.getData() != null) {
                Page<ReconciliationCompany> page = result.getData();
                if (page.getRecords() != null) {
                    return page.getRecords().stream()
                            .filter(company -> reconciliationCompanyNames.contains(company.getCompanyName()))
                            .collect(Collectors.toList());
                }
            }
        } catch (Exception e) {
            log.error("通过 Feign Client 获取对账公司数据失败: {}", e.getMessage(), e);
        }

        return new ArrayList<>();
    }

    /**
     * 查询保单号列表
     *
     * @param policyNosQueryRequest
     * @return 保单号列表
     */
    @PostMapping("/policyNos")
    @Operation(summary = "查询保单号列表")
    public Result<Page<PolicyNumberResponseVO>> queryPolicyNumbers(@RequestBody PolicyNosQueryRequest policyNosQueryRequest) {
        return Result.success(policyFollowService.queryPolicyNumbers(policyNosQueryRequest));
    }

    /**
     * 同步生成预计出账记录
     *
     * @param token 认证Token
     * @param policyNo 保单号
     */
    private void generateExpectedFortuneSync(String token, String policyNo) {
        // 设置ThreadLocal Token
        FeignTokenInterceptor.setThreadLocalToken(token);

        // 调用 ApiExpectedFortuneService 的 generateSyncWithLogAndRedis（同步版本，包含日志和Redis处理）
        ApiGenerateExpectedFortuneRequest generateExpectedFortuneRequest = new ApiGenerateExpectedFortuneRequest();
        generateExpectedFortuneRequest.setPolicyNo(policyNo);
        log.info("生成预计发佣generateSyncWithLogAndRedis——>PolicyNo：{}", policyNo);
        Result<ApiGenerateExpectedFortuneResponse> result = apiExpectedFortuneService.generateSyncWithLogAndRedis(generateExpectedFortuneRequest);
        if (result != null && result.getCode() == 200) {
            log.info("新单跟进-同步生成预计出账记录成功:保单号={}", policyNo);
        }
    }
}