package com.yd.csf.api.async;

import com.alibaba.fastjson.JSON;
import com.yd.common.enums.CommonEnum;
import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.csf.api.dto.AlgorithmCollectResDto;
import com.yd.csf.api.dto.AlgorithmResDto;
import com.yd.csf.api.service.ApiExpectedFortuneService;
import com.yd.csf.api.service.XxlJobService;
import com.yd.csf.feign.enums.FycSourceTypeEnum;
import com.yd.csf.feign.enums.OprSourceEnum;
import com.yd.csf.feign.request.expectedfortune.ApiGenerateExpectedFortuneRequest;
import com.yd.csf.feign.response.expectedfortune.ApiGenerateExpectedFortuneResponse;
import com.yd.csf.service.model.*;
import com.yd.csf.service.service.*;
import com.yd.feign.config.FeignTokenInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 发佣异步类
 */
@Slf4j
@Component
public class ApiExpectedFortuneAsyncService {

    @Resource
    private ApiExpectedFortuneService apiExpectedFortuneService;
    @Resource
    private PolicyFollowService policyFollowService;
    @Resource
    private ICalmTaskService iCalmTaskService;
    @Resource
    private XxlJobService xxlJobService;
    @Resource
    private IAgentAccumulatedFycService iAgentAccumulatedFycService;
    @Resource
    private IAgentDetailFycService iAgentDetailFycService;
    @Resource
    private IAgentAccumulatedFycLogService iAgentAccumulatedFycLogService;

    /**
     * 异步处理-执行预计发拥数据
     *
     * @return
     */
    @Async("commonAsyncExecutor")
    public Result execute(String policyNo, String token) {
        // 将 token 设置到 ThreadLocal 中，供 Feign 拦截器使用
        FeignTokenInterceptor.setThreadLocalToken(token);

        HttpServletRequest request1 = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token1 = request1.getHeader("Authorization");
        log.info("异步处理 token==============,{}",token1);

        try {
            ApiGenerateExpectedFortuneRequest request = new ApiGenerateExpectedFortuneRequest();
            request.setPolicyNo(policyNo);
            log.info("生成预计发佣 generateSyncWithLogAndRedis ——> PolicyNo：{}", policyNo);
            Result<ApiGenerateExpectedFortuneResponse> result =
                    apiExpectedFortuneService.generateSyncWithLogAndRedis(request);
            if (result != null && result.getCode() == 200) {
                log.info("新单跟进-同步生成预计出账记录成功: 保单号={}", policyNo);
            }
            return result;
        } finally {
            // 必须清理，防止内存泄漏
            FeignTokenInterceptor.clearThreadLocalToken();
        }
    }

    @Async("commonAsyncExecutor")
    public void buildCalmDateTask(List<AlgorithmCollectResDto> collectResDtos, String policyNo) {
        if (CollectionUtils.isEmpty(collectResDtos)) {
            log.info("构建冷静期定时任务，collectResDtos 为空，policyNo: {}", policyNo);
            return;
        }

        // 新单跟进对象
        PolicyFollow policyFollow = policyFollowService.queryOneByPolicyNo(policyNo);
        if (policyFollow == null) {
            log.warn("未找到保单跟进记录，policyNo: {}", policyNo);
            return;
        }

        // 过滤掉 calculatedValue 为 0 的项，并收集所有有效的 AlgorithmResDto，返回新的List<AlgorithmCollectResDto>
        List<AlgorithmCollectResDto> filteredCollectResDtos = collectResDtos.stream()
                .map(dto -> {
                    List<AlgorithmResDto> originalList = dto.getAlgorithmResDtoList();
                    if (CollectionUtils.isEmpty(originalList)) {
                        return null;
                    }
                    List<AlgorithmResDto> validList = originalList.stream()
                            .filter(res -> res.getCalculatedValue() != null
                                    && res.getCalculatedValue().compareTo(BigDecimal.ZERO) != 0)
                            .collect(Collectors.toList());
                    if (validList.isEmpty()) {
                        return null;
                    }
                    // 创建新对象
                    AlgorithmCollectResDto newDto = new AlgorithmCollectResDto();
                    BeanUtils.copyProperties(dto, newDto);
                    newDto.setAlgorithmResDtoList(validList);
                    return newDto;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        log.info("过滤掉 calculatedValue 为 0 的项，并收集所有有效的 AlgorithmResDto，返回新的List<AlgorithmCollectResDto>=>返回数据：{}", JSON.toJSONString(filteredCollectResDtos));
        if (CollectionUtils.isEmpty(filteredCollectResDtos)) {
            log.info("过滤后无有效发佣数据，policyNo: {}", policyNo);
            return;
        }

        //去重获取转介人业务ID列表
        List<String> clientUserBizIdList = filteredCollectResDtos
                .stream()
                .map(AlgorithmCollectResDto::getClientUserBizId)
                .distinct()
                .collect(Collectors.toList());
        //查询转介人对应累计积分
        List<AgentAccumulatedFyc> accumulatedFycList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(clientUserBizIdList)) {
            accumulatedFycList = iAgentAccumulatedFycService.queryList(clientUserBizIdList);
        }
        log.info("查询转介人对应累计积分=>返回数据：{}",JSON.toJSONString(accumulatedFycList));

        //循环，构建同保单号多个转介人多个期数多个佣金项目的冷静期定时任务记录和积分明细记录
        for (AlgorithmCollectResDto resDto : filteredCollectResDtos) {
            if (CollectionUtils.isEmpty(resDto.getAlgorithmResDtoList())) {
                continue;
            }
            List<AgentAccumulatedFyc> filterList = accumulatedFycList
                    .stream()
                    .filter(dto -> dto.getAgentId().equals(resDto.getClientUserBizId()))
                    .collect(Collectors.toList());
            AgentAccumulatedFyc agentAccumulatedFyc = null;
            if (CollectionUtils.isNotEmpty(filterList)) {
                agentAccumulatedFyc = filterList.get(0);
            }

            //积分解冻时间
            LocalDateTime thawingTime = resDto.getThawingTime();
            for (AlgorithmResDto algorithmResDto : resDto.getAlgorithmResDtoList()) {
                if (thawingTime != null && thawingTime.compareTo(LocalDateTime.now()) <= 0) {
                    //判断积分解冻时间（年月日 + 00:00:00）（冷静期时间 + 期数）是否小于等于当前时间（年月日时分秒）
                    //如果小于等于当前时间，不用走定时任务来解冻积分，应该及时生效积分

                    //业务员积分明细表唯一业务ID
                    String detailFycBizId = RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_AGENT_DETAIL_FYC.getCode());
                    //新增积分明细
                    //status 定时任务0-冻结。非定时任务1-生效
                    addAgentDetailFyc(resDto,policyNo,algorithmResDto,detailFycBizId,agentAccumulatedFyc,1);

                }else if (thawingTime != null && thawingTime.compareTo(LocalDateTime.now()) > 0){
                    //判断积分解冻时间（年月日 + 00:00:00）（冷静期时间 + 期数）是否大于当前时间（年月日时分秒）
                    //如果大于当前时间，走定时任务来解冻积分。
                    CalmTask calmTask = new CalmTask();
                    //保单号
                    calmTask.setPolicyNo(policyNo);
                    //新单跟进唯一业务ID
                    calmTask.setPolicyBizId(policyFollow.getPolicyBizId());
                    //执行时间（定时任务执行时间） = 积分解冻时间
                    calmTask.setExecutionTime(resDto.getThawingTime());
                    //冷静期天数
                    calmTask.setCoolingOffDays(policyFollow.getCoolingOffDays());
                    //保单冷静期定时任务表唯一业务ID
                    calmTask.setCalmTaskBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_CALM_TASK.getCode()));
                    //执行状态（0-未执行，1-执行中，2-已执行，3-执行异常）
                    //0-未执行
                    calmTask.setExecutionStatus("0");
                    //转介人名称
                    calmTask.setBrokerName(resDto.getClientUserName());
                    //转介人业务id
                    calmTask.setBrokerBizId(resDto.getClientUserBizId());
                    //佣金项目
                    calmTask.setCommissionName(algorithmResDto.getItemName());
                    //发佣期数
                    calmTask.setFortunePeriod(resDto.getFortunePeriod());
                    //佣金值（积分值）
                    calmTask.setFyc(algorithmResDto.getCalculatedValue());
                    //冷静期结束日期
                    calmTask.setCoolingOffEndDate(policyFollow.getCoolingOffEndDate());
                    //业务员积分明细表唯一业务ID
                    String detailFycBizId = RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_AGENT_DETAIL_FYC.getCode());
                    calmTask.setDetailFycBizId(detailFycBizId);
                    iCalmTaskService.saveOrUpdate(calmTask);

                    //新增积分明细
                    //status 定时任务0-冻结。非定时任务1-生效
                    addAgentDetailFyc(resDto,policyNo,algorithmResDto,detailFycBizId,agentAccumulatedFyc,0);

                    //创建XXL-Job定时任务
                    xxlJobService.addScheduleJob(calmTask.getCalmTaskBizId(),"冷静期定时发送任务-","calmSendJobHandler",Date.from(resDto.getThawingTime().atZone(ZoneId.systemDefault()).toInstant()));
                }
            }
        }

        //更新积分总表的值
        updateAgentAccumulatedFyc(accumulatedFycList,filteredCollectResDtos);

    }

    /**
     * 新增积分明细
     * @param resDto
     * @param policyNo
     * @param algorithmResDto
     * @param detailFycBizId
     * @param agentAccumulatedFyc
     * @param status 定时任务0-冻结。非定时任务1-生效
     */
    public void addAgentDetailFyc(AlgorithmCollectResDto resDto,
                                  String policyNo,
                                  AlgorithmResDto algorithmResDto,
                                  String detailFycBizId,
                                  AgentAccumulatedFyc agentAccumulatedFyc,
                                  Integer status) {
        //新增积分明细
        AgentDetailFyc agentDetailFyc = new AgentDetailFyc();
        //积分来源类型
        agentDetailFyc.setSourceType(FycSourceTypeEnum.CALM_TASK.getItemValue());
        //业务员ID
        agentDetailFyc.setAgentId(resDto.getClientUserBizId());
        //积分提供的保单号
        agentDetailFyc.setPolicyNo(policyNo);
        //积分来源基本法项目配置表唯一业务ID
        agentDetailFyc.setRuleItemBizId(algorithmResDto.getRuleItemBizId());
        //业务员积分明细表唯一业务ID
        agentDetailFyc.setDetailFycBizId(detailFycBizId);
        //状态(0:停用（冻结） 1:启用（生效）)
        //冻结
        agentDetailFyc.setStatus(status);
        //积分解冻时间
        agentDetailFyc.setThawingTime(resDto.getThawingTime());
        //变化前的业务员总FYC积分（已生效+未生效）
        agentDetailFyc.setBeforeFyc(agentAccumulatedFyc != null ? agentAccumulatedFyc.getTotalFyc() : BigDecimal.ZERO);
        //变化值（-代表减少）
        agentDetailFyc.setChangeFyc(algorithmResDto.getCalculatedValue().toString());
        //变化后的业务员总FYC积分
        agentDetailFyc.setAfterFyc(agentDetailFyc.getBeforeFyc().add(algorithmResDto.getCalculatedValue()));
        iAgentDetailFycService.saveOrUpdate(agentDetailFyc);

    }

    /**
     * 更新积分总表的值
     * @param accumulatedFycList
     * @param filteredCollectResDtos
     */
    public void updateAgentAccumulatedFyc(List<AgentAccumulatedFyc> accumulatedFycList,
                                          List<AlgorithmCollectResDto> filteredCollectResDtos) {
        log.info("更新积分总表的值=>accumulatedFycList入参：{}",JSON.toJSONString(accumulatedFycList));
        log.info("更新积分总表的值=>filteredCollectResDtos入参：{}",JSON.toJSONString(filteredCollectResDtos));
        //更新积分总表的值，更新这个accumulatedFycList的每个对象里面的：未生效累计积分（不限业务场景）、累计积分 = 未生效累计积分 + 已生效累计积分、未生效累计首期佣金积分值（佣金场景）、未生效累计非首期佣金积分值（佣金场景）
        // 将查询到的累计积分列表转为 Map（便于更新）
        Map<String, AgentAccumulatedFyc> fycMap = accumulatedFycList.stream()
                .collect(Collectors.toMap(
                        AgentAccumulatedFyc::getAgentId,
                        Function.identity(),
                        (v1, v2) -> v1
                ));
        log.info("将查询到的累计积分列表转为 Map=>fycMap：{}",JSON.toJSONString(fycMap));

        // 构建每个转介人本次的积分增量 Map
        // 未生效个人累计积分增量（定时（冻结））
        Map<String, BigDecimal> noDeltaMap = new HashMap<>();
        // 未生效首期佣金增量（定时（冻结））
        Map<String, BigDecimal> noFirstCommissionDeltaMap = new HashMap<>();
        // 未生效非首期佣金增量（定时（冻结））
        Map<String, BigDecimal> noRycDeltaMap = new HashMap<>();

        // 已生效个人累计积分增量（非定时（生效））
        Map<String, BigDecimal> deltaMap = new HashMap<>();
        // 已生效首期佣金增量（非定时（生效））
        Map<String, BigDecimal> firstCommissionDeltaMap = new HashMap<>();
        // 已生效非首期佣金增量（非定时（生效））
        Map<String, BigDecimal> rycDeltaMap = new HashMap<>();

        //收集去重的转介人业务ID
        HashSet<String> hashSet = new HashSet<>();

        for (AlgorithmCollectResDto resDto : filteredCollectResDtos) {
            String agentId = resDto.getClientUserBizId();
            Integer fortunePeriod = resDto.getFortunePeriod();

            hashSet.add(agentId);

            //积分解冻时间
            LocalDateTime thawingTime = resDto.getThawingTime();
            for (AlgorithmResDto algorithmResDto : resDto.getAlgorithmResDtoList()) {
                BigDecimal val = algorithmResDto.getCalculatedValue();
                if (val == null) continue;

                if (thawingTime != null && thawingTime.compareTo(LocalDateTime.now()) <= 0) {
                    //判断积分解冻时间（年月日 + 00:00:00）（冷静期时间 + 期数）是否小于等于当前时间（年月日时分秒）
                    //如果小于等于当前时间，不用走定时任务来解冻积分，应该及时生效积分
                    // 已生效个人累计积分增量（非定时（生效））
                    deltaMap.merge(agentId, val, BigDecimal::add);
                    // 根据期数判断佣金类型
                    if (fortunePeriod != null && fortunePeriod == 1) {
                        //已生效首期佣金增量（非定时（生效））
                        firstCommissionDeltaMap.merge(agentId, val, BigDecimal::add);
                    } else {
                        //已生效非首期佣金增量（非定时（生效））
                        rycDeltaMap.merge(agentId, val, BigDecimal::add);
                    }

                }else if (thawingTime != null && thawingTime.compareTo(LocalDateTime.now()) > 0){
                    //判断积分解冻时间（年月日 + 00:00:00）（冷静期时间 + 期数）是否大于当前时间（年月日时分秒）
                    //如果大于当前时间，走定时任务来解冻积分。
                    // 未生效个人累计积分增量（定时（冻结））
                    noDeltaMap.merge(agentId, val, BigDecimal::add);
                    // 根据期数判断佣金类型
                    if (fortunePeriod != null && fortunePeriod == 1) {
                        //未生效首期佣金增量（定时（冻结））
                        noFirstCommissionDeltaMap.merge(agentId, val, BigDecimal::add);
                    } else {
                        //未生效非首期佣金增量（定时（冻结））
                        noRycDeltaMap.merge(agentId, val, BigDecimal::add);
                    }
                }
            }
        }
        log.info("收集去重的转介人业务ID=>hashSet：{}",JSON.toJSONString(hashSet));

        log.info("未生效个人累计积分增量（定时（冻结））=>noDeltaMap：{}",JSON.toJSONString(noDeltaMap));
        log.info("未生效首期佣金增量（定时（冻结））=>noFirstCommissionDeltaMap：{}",JSON.toJSONString(noFirstCommissionDeltaMap));
        log.info("未生效非首期佣金增量（定时（冻结））=>noRycDeltaMap：{}",JSON.toJSONString(noRycDeltaMap));
        log.info("已生效个人累计积分增量（非定时（生效））=>deltaMap：{}",JSON.toJSONString(deltaMap));
        log.info("已生效首期佣金增量（非定时（生效））=>firstCommissionDeltaMap：{}",JSON.toJSONString(firstCommissionDeltaMap));
        log.info("已生效非首期佣金增量（非定时（生效））=>rycDeltaMap：{}",JSON.toJSONString(rycDeltaMap));

        // 更新或新建累计积分记录
        List<AgentAccumulatedFyc> toUpdateList = new ArrayList<>();
        for (String agentId : hashSet) {
            //已生效个人累计积分增量（非定时（生效））
            BigDecimal delta = deltaMap.get(agentId);
            //未生效个人累计积分增量（定时（冻结））
            BigDecimal noDelta = noDeltaMap.get(agentId);

            //未生效首期佣金增量（定时（冻结））
            BigDecimal noFirstDelta = noFirstCommissionDeltaMap.getOrDefault(agentId, BigDecimal.ZERO);
            //未生效非首期佣金增量（定时（冻结））
            BigDecimal noRycDelta = noRycDeltaMap.getOrDefault(agentId, BigDecimal.ZERO);

            //已生效首期佣金增量（非定时（生效））
            BigDecimal firstDelta = firstCommissionDeltaMap.getOrDefault(agentId, BigDecimal.ZERO);
            //已生效非首期佣金增量（非定时（生效））
            BigDecimal rycDelta = rycDeltaMap.getOrDefault(agentId, BigDecimal.ZERO);

            AgentAccumulatedFyc fyc = fycMap.get(agentId);
            if (fyc == null) {
                continue;
            }
            // 应用增量
            //未生效累计积分（不限业务场景）
            fyc.setNoEffect(fyc.getNoEffect().add(noDelta));
            //未生效累计首期佣金积分值（佣金场景）
            fyc.setNoFirstCommission(fyc.getNoFirstCommission().add(noFirstDelta));
            //未生效累计非首期佣金积分值（佣金场景）
            fyc.setNoRyc(fyc.getNoRyc().add(noRycDelta));

            //已生效累计积分（不限业务场景）
            fyc.setEffect(fyc.getEffect().add(delta));
            //已生效累计首期佣金积分值（佣金场景）
            fyc.setFirstCommission(fyc.getFirstCommission().add(firstDelta));
            //已生效累计非首期佣金积分值（佣金场景）
            fyc.setRyc(fyc.getRyc().add(rycDelta));

            //累计积分 = 未生效累计积分 + 已生效累计积分
            fyc.setTotalFyc(fyc.getNoEffect().add(fyc.getEffect()));
            //晋升职级累计积分 = 已生效累计积分 - 已生效累计非首期佣金积分值
            BigDecimal promotion = fyc.getEffect().subtract(fyc.getRyc());
            fyc.setPromotion(promotion.compareTo(BigDecimal.ZERO) <= 0 ?  BigDecimal.ZERO : promotion);
            //最后计算日期，记录最后一次计算累计FYC的日期
            fyc.setLastCalcDate(LocalDateTime.now());
            toUpdateList.add(fyc);
        }

        log.info("更新或新建累计积分记录=>toUpdateList：{}",JSON.toJSONString(toUpdateList));

        // 批量保存
        if (CollectionUtils.isNotEmpty(toUpdateList)) {
            iAgentAccumulatedFycService.saveOrUpdateBatch(toUpdateList);
            log.info("更新业务员累计积分完成，涉及人数: {}", toUpdateList.size());

            //批量新增积分总表日志表
            List<AgentAccumulatedFycLog> saveLogList = toUpdateList.stream().map(dto -> {
                AgentAccumulatedFycLog log = new AgentAccumulatedFycLog();
                BeanUtils.copyProperties(dto,log);
                log.setId(null);
                log.setFycBizId(dto.getFycBizId());
                log.setFycLogBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_AGENT_ACCUMULATED_FYC_LOG.getCode()));
                //操作来源枚举-冻结
                log.setSourceType(OprSourceEnum.CALM_TASK_FREEZE.getItemValue());
                log.setCreateTime(LocalDateTime.now());
                log.setUpdateTime(LocalDateTime.now());
                return log;
            }).collect(Collectors.toList());
            iAgentAccumulatedFycLogService.saveOrUpdateBatch(saveLogList);
        }
    }


}