package com.sundot.airport.exam.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sundot.airport.common.dto.ModuleStatisticsDTO;
import com.sundot.airport.common.dto.QuestionAllocationDTO;
import com.sundot.airport.common.dto.WeakModuleDetailDTO;
import com.sundot.airport.common.exception.ServiceException;
import com.sundot.airport.common.service.ICheckProblemStatisticsService;
import com.sundot.airport.exam.domain.Ques;
import com.sundot.airport.exam.domain.QuesCat;
import com.sundot.airport.exam.domain.WeakModuleRule;
import com.sundot.airport.exam.mapper.DailyTaskDetailMapper;
import com.sundot.airport.exam.service.IWeakModuleRuleService;
import com.sundot.airport.exam.service.IWeakModuleStatisticsService;
import com.sundot.airport.exam.common.service.QuesCatService;
import com.sundot.airport.exam.common.service.QuesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <b>功能名：</b>WeakModuleStatisticsServiceImpl<br>
 * <b>说明：</b> 薄弱模块统计Service实现（用于智能出题） <br>
 * <b>著作权：</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
 * <b>修改履历：
 *
 * @author Simon Lin
 */
@Service
public class WeakModuleStatisticsServiceImpl implements IWeakModuleStatisticsService {

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

    @Autowired
    private DailyTaskDetailMapper dailyTaskDetailMapper;

    @Autowired
    private ICheckProblemStatisticsService checkProblemStatisticsService;

    @Autowired
    private IWeakModuleRuleService weakModuleRuleService;

    @Autowired
    private QuesCatService quesCatService;

    @Autowired
    private QuesService quesService;

    @Override
    public List<ModuleStatisticsDTO> analyzeTop3WeakModules(Long userId, Integer statsDays) {
        log.info("开始分析用户 {} 的薄弱模块，统计天数：{}", userId, statsDays);

        // 1. 获取启用的规则
        WeakModuleRule rule = weakModuleRuleService.getActiveRule();
        if (rule == null) {
            throw new ServiceException("没有启用的薄弱项规则，请先配置并启用规则");
        }

        // 2. 计算时间范围
        Date endDate = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endDate);
        calendar.add(Calendar.DAY_OF_MONTH, -statsDays);
        Date startDate = calendar.getTime();

        log.info("统计时间范围：{} 至 {}", startDate, endDate);

        // 3. 获取错题统计
        List<ModuleStatisticsDTO> wrongStats = dailyTaskDetailMapper
                .countWrongAnswersByModule(userId, startDate, endDate);
        log.info("错题统计结果：共 {} 个模块", wrongStats != null ? wrongStats.size() : 0);

        // 4. 获取巡检问题统计
        List<ModuleStatisticsDTO> problemStats = checkProblemStatisticsService
                .countCheckProblemsByModule(userId, startDate, endDate);
        log.info("巡检问题统计结果：共 {} 个模块", problemStats != null ? problemStats.size() : 0);

        // 5. 合并统计数据（按模块名称merge）
        Map<String, ModuleStatisticsDTO> moduleMap = new HashMap<>();

        // 处理错题统计
        if (CollUtil.isNotEmpty(wrongStats)) {
            for (ModuleStatisticsDTO wrong : wrongStats) {
                if (wrong.getModuleName() != null && !wrong.getModuleName().isEmpty()) {
                    moduleMap.put(wrong.getModuleName(), wrong);
                }
            }
        }

        // 处理巡检问题统计
        if (CollUtil.isNotEmpty(problemStats)) {
            for (ModuleStatisticsDTO problem : problemStats) {
                String moduleName = problem.getModuleName();
                if (moduleName != null && !moduleName.isEmpty()) {
                    if (moduleMap.containsKey(moduleName)) {
                        // 模块已存在，更新巡检问题数
                        moduleMap.get(moduleName).setProblemCount(problem.getProblemCount());
                    } else {
                        // 模块不存在，创建新记录
                        ModuleStatisticsDTO dto = new ModuleStatisticsDTO();
                        dto.setModuleName(moduleName);
                        dto.setWrongCount(0);
                        dto.setProblemCount(problem.getProblemCount());
                        moduleMap.put(moduleName, dto);
                    }
                }
            }
        }

        if (moduleMap.isEmpty()) {
            log.warn("用户 {} 在指定时间范围内没有错题和巡检问题记录", userId);
            return new ArrayList<>();
        }

        // 6. 计算综合影响分
        BigDecimal wrongWeight = rule.getWmrWrongAnswerWeight();
        BigDecimal problemWeight = rule.getWmrCheckProblemWeight();

        log.info("使用规则：错题权重={}, 巡检问题权重={}", wrongWeight, problemWeight);

        for (ModuleStatisticsDTO dto : moduleMap.values()) {
            BigDecimal wrongScore = new BigDecimal(dto.getWrongCount()).multiply(wrongWeight);
            BigDecimal problemScore = new BigDecimal(dto.getProblemCount()).multiply(problemWeight);
            BigDecimal influenceScore = wrongScore.add(problemScore);
            dto.setInfluenceScore(influenceScore);

            log.debug("模块 [{}]: 错题数={}, 巡检问题数={}, 综合影响分={}",
                    dto.getModuleName(), dto.getWrongCount(), dto.getProblemCount(), influenceScore);
        }

        // 7. 按影响度降序排序，取TOP3
        List<ModuleStatisticsDTO> top3 = moduleMap.values().stream()
                .sorted((a, b) -> b.getInfluenceScore().compareTo(a.getInfluenceScore()))
                .limit(3)
                .collect(Collectors.toList());

        log.info("TOP3薄弱模块分析完成：");
        for (int i = 0; i < top3.size(); i++) {
            ModuleStatisticsDTO dto = top3.get(i);
            log.info("  TOP{}: {} (影响分={}, 错题={}, 巡检问题={})",
                    i + 1, dto.getModuleName(), dto.getInfluenceScore(),
                    dto.getWrongCount(), dto.getProblemCount());
        }

        return top3;
    }

    @Override
    public List<ModuleStatisticsDTO> analyzeTop3WeakModulesWithActiveRule(Long userId) {
        // 获取启用的规则并使用其配置的统计天数
        WeakModuleRule rule = weakModuleRuleService.getActiveRule();
        if (rule == null) {
            throw new ServiceException("没有启用的薄弱项规则，请先配置并启用规则");
        }

        Integer statsDays = rule.getWmrStatsDays();
        log.info("使用启用规则的统计天数：{} 天", statsDays);

        return analyzeTop3WeakModules(userId, statsDays);
    }

    @Override
    public List<WeakModuleDetailDTO> getTop3WeakModulesDetail(Long userId) {
        log.info("获取用户 {} 的薄弱模块详细信息", userId);

        // 1. 获取启用的规则
        WeakModuleRule rule = weakModuleRuleService.getActiveRule();
        if (rule == null) {
            throw new ServiceException("没有启用的薄弱项规则，请先配置并启用规则");
        }

        Integer statsDays = rule.getWmrStatsDays();
        BigDecimal wrongWeight = rule.getWmrWrongAnswerWeight();
        BigDecimal problemWeight = rule.getWmrCheckProblemWeight();

        log.info("使用规则：统计天数={} 天, 错题权重={}, 巡检问题权重={}",
                statsDays, wrongWeight, problemWeight);

        // 2. 计算时间范围
        Date endDate = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endDate);
        calendar.add(Calendar.DAY_OF_MONTH, -statsDays);
        Date startDate = calendar.getTime();

        // 3. 获取错题统计
        List<ModuleStatisticsDTO> wrongStats = dailyTaskDetailMapper
                .countWrongAnswersByModule(userId, startDate, endDate);
        log.info("错题统计结果：共 {} 个模块", wrongStats != null ? wrongStats.size() : 0);

        // 4. 获取巡检问题统计
        List<ModuleStatisticsDTO> problemStats = checkProblemStatisticsService
                .countCheckProblemsByModule(userId, startDate, endDate);
        log.info("巡检问题统计结果：共 {} 个模块", problemStats != null ? problemStats.size() : 0);

        // 5. 合并统计数据
        Map<String, ModuleStatisticsDTO> moduleMap = new HashMap<>();

        // 处理错题统计
        if (CollUtil.isNotEmpty(wrongStats)) {
            for (ModuleStatisticsDTO wrong : wrongStats) {
                if (wrong.getModuleName() != null && !wrong.getModuleName().isEmpty()) {
                    moduleMap.put(wrong.getModuleName(), wrong);
                }
            }
        }

        // 处理巡检问题统计
        if (CollUtil.isNotEmpty(problemStats)) {
            for (ModuleStatisticsDTO problem : problemStats) {
                String moduleName = problem.getModuleName();
                if (moduleName != null && !moduleName.isEmpty()) {
                    if (moduleMap.containsKey(moduleName)) {
                        moduleMap.get(moduleName).setProblemCount(problem.getProblemCount());
                    } else {
                        ModuleStatisticsDTO dto = new ModuleStatisticsDTO();
                        dto.setModuleName(moduleName);
                        dto.setWrongCount(0);
                        dto.setProblemCount(problem.getProblemCount());
                        moduleMap.put(moduleName, dto);
                    }
                }
            }
        }

        if (moduleMap.isEmpty()) {
            log.warn("用户 {} 在指定时间范围内没有错题和巡检问题记录", userId);
            return new ArrayList<>();
        }

        // 6. 计算综合影响分并转换为详细DTO
        List<WeakModuleDetailDTO> detailList = new ArrayList<>();

        for (ModuleStatisticsDTO stats : moduleMap.values()) {
            WeakModuleDetailDTO detail = new WeakModuleDetailDTO();

            // 基础信息
            detail.setModuleId(stats.getModuleId());
            detail.setModuleName(stats.getModuleName());
            detail.setWrongCount(stats.getWrongCount());
            detail.setProblemCount(stats.getProblemCount());

            // 规则配置
            detail.setWrongAnswerWeight(wrongWeight);
            detail.setCheckProblemWeight(problemWeight);
            detail.setStatsDays(statsDays);

            // 计算得分
            BigDecimal wrongScore = new BigDecimal(stats.getWrongCount())
                    .multiply(wrongWeight)
                    .setScale(2, RoundingMode.HALF_UP);
            BigDecimal problemScore = new BigDecimal(stats.getProblemCount())
                    .multiply(problemWeight)
                    .setScale(2, RoundingMode.HALF_UP);
            BigDecimal influenceScore = wrongScore.add(problemScore)
                    .setScale(2, RoundingMode.HALF_UP);

            detail.setWrongScore(wrongScore);
            detail.setProblemScore(problemScore);
            detail.setInfluenceScore(influenceScore);

            // 生成计算公式说明
            String formula = String.format(
                    "错题得分(%s × %s) + 巡检问题得分(%s × %s) = %s + %s = %s",
                    stats.getWrongCount(), wrongWeight,
                    stats.getProblemCount(), problemWeight,
                    wrongScore, problemScore, influenceScore
            );
            detail.setCalculationFormula(formula);

            detailList.add(detail);

            log.debug("模块 [{}]: {}", stats.getModuleName(), formula);
        }

        // 7. 按影响度降序排序，取TOP3
        List<WeakModuleDetailDTO> top3 = detailList.stream()
                .sorted((a, b) -> b.getInfluenceScore().compareTo(a.getInfluenceScore()))
                .limit(3)
                .collect(Collectors.toList());

        // 8. 设置排名
        for (int i = 0; i < top3.size(); i++) {
            top3.get(i).setRanking(i + 1);
        }

        log.info("TOP3薄弱模块详细信息获取完成：");
        for (WeakModuleDetailDTO detail : top3) {
            log.info("  TOP{}: {} (影响分={}, 错题={}, 巡检问题={})",
                    detail.getRanking(), detail.getModuleName(),
                    detail.getInfluenceScore(), detail.getWrongCount(), detail.getProblemCount());
            log.info("    公式: {}", detail.getCalculationFormula());
        }

        return top3;
    }

    @Override
    public List<WeakModuleDetailDTO> getTop3WeakModulesDetailWithAllocation(Long userId, int questionCount) {
        log.info("获取用户 {} 的薄弱模块详细信息及出题计划（题目数={}）", userId, questionCount);

        // 1. 先获取基础的薄弱模块详情
        List<WeakModuleDetailDTO> details = getTop3WeakModulesDetail(userId);

        if (details.isEmpty()) {
            return details;
        }

        // 2. 获取规则配置
        WeakModuleRule rule = weakModuleRuleService.getActiveRule();
        BigDecimal[] ratios = {
                rule.getWmrTop1Ratio(),
                rule.getWmrTop2Ratio(),
                rule.getWmrTop3Ratio()
        };

        // 3. 计算题目分配计划（使用与QuestionSelectionServiceImpl相同的算法）
        Map<String, Integer> moduleQuestionCountMap = calculateModuleQuestionCountForDetail(
                details, ratios, questionCount);

        // 4. 为每个模块添加出题计划详情
        List<QuestionAllocationDTO> allAllocations = new ArrayList<>();

        for (int i = 0; i < details.size(); i++) {
            WeakModuleDetailDTO detail = details.get(i);
            String moduleName = detail.getModuleName();
            Integer plannedCount = moduleQuestionCountMap.getOrDefault(moduleName, 0);

            // 设置TOP3占比配置
            detail.setTop1Ratio(rule.getWmrTop1Ratio());
            detail.setTop2Ratio(rule.getWmrTop2Ratio());
            detail.setTop3Ratio(rule.getWmrTop3Ratio());

            // 查询题库可用题目数
            int availableCount = getAvailableQuestionCount(moduleName);

            // 创建分配详情
            QuestionAllocationDTO allocation = new QuestionAllocationDTO();
            allocation.setModuleName(moduleName);
            allocation.setRanking(detail.getRanking());
            allocation.setConfiguredRatio(ratios[i]);
            allocation.setPlannedCount(plannedCount);
            allocation.setActualCount(Math.min(plannedCount, availableCount)); // 实际能分配的题目数
            allocation.setAvailableCount(availableCount);

            // 计算实际占比
            BigDecimal actualRatio = questionCount > 0
                    ? new BigDecimal(allocation.getActualCount())
                    .divide(new BigDecimal(questionCount), 4, RoundingMode.HALF_UP)
                    : BigDecimal.ZERO;
            allocation.setActualRatio(actualRatio);

            // 生成分配说明
            String note = String.format(
                    "配置占比%s，计划分配%d题，题库可用%d题，实际可分配%d题（实际占比%s）",
                    allocation.getConfiguredRatio(),
                    plannedCount,
                    availableCount,
                    allocation.getActualCount(),
                    actualRatio
            );
            allocation.setAllocationNote(note);

            allAllocations.add(allocation);

            log.info("  模块 [{}]: {}", moduleName, note);
        }

        // 5. 将分配列表设置到第一个detail对象中（作为汇总）
        if (!details.isEmpty()) {
            details.get(0).setQuestionAllocations(allAllocations);
        }

        return details;
    }

    /**
     * 计算每个薄弱模块的题目数量（与QuestionSelectionServiceImpl中的算法一致）
     * 注意：TOP3占比之和通常不是100%，剩余题目从其他模块随机选择
     */
    private Map<String, Integer> calculateModuleQuestionCountForDetail(
            List<WeakModuleDetailDTO> details,
            BigDecimal[] ratios,
            int totalQuestionCount) {

        Map<String, Integer> result = new LinkedHashMap<>();

        // 按照配置的占比分配题目（向下取整）
        int allocatedCount = 0;

        for (int i = 0; i < Math.min(details.size(), 3); i++) {
            WeakModuleDetailDTO detail = details.get(i);
            BigDecimal ratio = ratios[i];
            String moduleName = detail.getModuleName();

            // 向下取整分配题目
            int count = ratio.multiply(new BigDecimal(totalQuestionCount))
                    .setScale(0, RoundingMode.DOWN)
                    .intValue();

            if (count > 0) {
                result.put(moduleName, count);
                allocatedCount += count;
            }
        }

        log.info("TOP3模块分配：{} 道题，剩余 {} 道题将从其他模块随机选择",
                allocatedCount, totalQuestionCount - allocatedCount);

        // 注意：剩余的题目不在这里处理，由调用方从其他模块随机选择

        return result;
    }

    /**
     * 获取指定模块的可用题目数量
     */
    private int getAvailableQuestionCount(String moduleName) {
        // 1. 根据模块名称查找对应的 qc_id
        LambdaQueryWrapper<QuesCat> catWrapper = new LambdaQueryWrapper<>();
        catWrapper.eq(QuesCat::getQcName, moduleName);
        catWrapper.eq(QuesCat::getDelStatus, 0);
        List<QuesCat> categories = quesCatService.list(catWrapper);

        if (CollUtil.isEmpty(categories)) {
            return 0;
        }

        // 2. 统计该分类下的题目数量
        List<String> qcIds = categories.stream()
                .map(QuesCat::getQcId)
                .collect(Collectors.toList());

        LambdaQueryWrapper<Ques> quesWrapper = new LambdaQueryWrapper<>();
        quesWrapper.in(Ques::getQcId, qcIds);
        quesWrapper.eq(Ques::getDelStatus, 0);

        return (int) quesService.count(quesWrapper);
    }
}
