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

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sundot.airport.exam.domain.*;
import com.sundot.airport.exam.mapper.DailyTaskDetailMapper;
import com.sundot.airport.exam.mapper.WeakModuleAnalysisMapper;
import com.sundot.airport.exam.common.service.QuesCatService;
import com.sundot.airport.exam.service.IWeakModuleAnalysisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <b>功能名：</b>WeakModuleAnalysisServiceImpl<br>
 * <b>说明：</b> 薄弱模块分析Service实现 <br>
 * <b>著作权：</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
 * <b>修改履历：
 *
 * @author Simon Lin
 */
@Service
public class WeakModuleAnalysisServiceImpl implements IWeakModuleAnalysisService {

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

    @Autowired
    private WeakModuleAnalysisMapper weakModuleAnalysisMapper;

    @Autowired
    private DailyTaskDetailMapper dailyTaskDetailMapper;

    @Autowired
    private QuesCatService quesCatService;

    @Autowired
    private com.sundot.airport.system.service.IBaseCheckCategoryService baseCheckCategoryService;

    /**
     * 薄弱分类阈值：最近3次正确率低于60%
     */
    private static final BigDecimal WEAK_THRESHOLD = new BigDecimal("0.6");

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void updateUserWeakModuleAnalysis(Long userId) {
        log.info("开始更新用户薄弱分类分析（按一级分类） - userId: {}", userId);

        // 1. 查询所有一级分类（base_check_category表，level=1）
        com.sundot.airport.system.domain.BaseCheckCategory queryParam = new com.sundot.airport.system.domain.BaseCheckCategory();
        queryParam.setLevel(1);
        List<com.sundot.airport.system.domain.BaseCheckCategory> level1Categories =
                baseCheckCategoryService.selectBaseCheckCategoryList(queryParam);

        if (level1Categories.isEmpty()) {
            log.warn("一级分类未配置");
            return;
        }

        // 2. 首先查询用户的所有任务ID
        LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.eq(DailyTask::getDtUserId, userId);
        taskWrapper.eq(DailyTask::getDelStatus, 0);
        taskWrapper.select(DailyTask::getDtId);

        // 使用Spring获取DailyTaskMapper
        com.sundot.airport.exam.mapper.DailyTaskMapper dailyTaskMapper =
                com.sundot.airport.common.utils.spring.SpringUtils.getBean(
                        com.sundot.airport.exam.mapper.DailyTaskMapper.class);
        List<DailyTask> userTasks = dailyTaskMapper.selectList(taskWrapper);

        if (userTasks.isEmpty()) {
            log.info("用户暂无任务记录 - userId: {}", userId);
            return;
        }

        List<String> taskIds = userTasks.stream()
                .map(DailyTask::getDtId)
                .collect(java.util.stream.Collectors.toList());

        // 3. 查询这些任务的所有答题明细（已答题的题目）
        LambdaQueryWrapper<DailyTaskDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(DailyTaskDetail::getDtdTaskId, taskIds);
        wrapper.eq(DailyTaskDetail::getDelStatus, 0);
        wrapper.isNotNull(DailyTaskDetail::getDtdIsCorrect);
        wrapper.orderByDesc(DailyTaskDetail::getDtdAnswerTime);
        List<DailyTaskDetail> allDetails = dailyTaskDetailMapper.selectList(wrapper);

        if (allDetails.isEmpty()) {
            log.info("用户暂无答题记录 - userId: {}", userId);
            return;
        }

        // 4. 按一级分类分组统计（通过二级分类名称映射到一级分类）
        Map<String, List<DailyTaskDetail>> recordsByLevel1Category = groupRecordsByLevel1Category(allDetails);

        // 5. 查询用户现有的薄弱分类分析记录
        LambdaQueryWrapper<WeakModuleAnalysis> analysisWrapper = new LambdaQueryWrapper<>();
        analysisWrapper.eq(WeakModuleAnalysis::getWmaUserId, userId);
        analysisWrapper.eq(WeakModuleAnalysis::getDelStatus, 0);
        List<WeakModuleAnalysis> existingAnalysis = weakModuleAnalysisMapper.selectList(analysisWrapper);

        Map<String, WeakModuleAnalysis> existingMap = existingAnalysis.stream()
                .collect(Collectors.toMap(
                        WeakModuleAnalysis::getWmaModuleId,
                        a -> a,
                        (v1, v2) -> v1 // 如果有重复key，保留第一个
                ));

        // 6. 遍历每个一级分类，计算统计数据
        for (com.sundot.airport.system.domain.BaseCheckCategory level1Category : level1Categories) {
            String categoryId = String.valueOf(level1Category.getId());
            String categoryName = level1Category.getName();

            List<DailyTaskDetail> categoryRecords = recordsByLevel1Category.getOrDefault(categoryId, new ArrayList<>());

            if (categoryRecords.isEmpty()) {
                // 如果该一级分类没有答题记录，跳过
                continue;
            }

            // 计算全部统计数据
            int totalCount = categoryRecords.size();
            long correctCount = categoryRecords.stream()
                    .filter(detail -> Boolean.TRUE.equals(detail.getDtdIsCorrect()))
                    .count();
            BigDecimal correctRate = calculateRate(correctCount, totalCount);

            // 计算最近3次统计数据
            List<DailyTaskDetail> last3Records = categoryRecords.stream()
                    .limit(3)
                    .collect(Collectors.toList());

            int last3Total = last3Records.size();
            long last3Correct = last3Records.stream()
                    .filter(detail -> Boolean.TRUE.equals(detail.getDtdIsCorrect()))
                    .count();
            BigDecimal last3Rate = calculateRate(last3Correct, last3Total);

            // 判断是否为薄弱分类
            boolean isWeak = last3Rate.compareTo(WEAK_THRESHOLD) < 0;

            // 更新或插入记录
            WeakModuleAnalysis analysis = existingMap.get(categoryId);
            if (analysis == null) {
                // 新增
                analysis = new WeakModuleAnalysis();
                analysis.setWmaId(IdUtil.getSnowflakeNextIdStr());
                analysis.setWmaUserId(userId);
                analysis.setWmaModuleId(categoryId);
                analysis.setWmaModuleName(categoryName);
                analysis.setWmaTotalCount(totalCount);
                analysis.setWmaCorrectCount((int) correctCount);
                analysis.setWmaCorrectRate(correctRate);
                analysis.setWmaLast3Total(last3Total);
                analysis.setWmaLast3Correct((int) last3Correct);
                analysis.setWmaLast3Rate(last3Rate);
                analysis.setWmaIsWeak(isWeak);
                analysis.setWmaUpdateTime(new Date());
                analysis.setDelStatus(0);
                analysis.setCreateTime(new Date());
                analysis.setCreateUid(userId.toString());

                weakModuleAnalysisMapper.insert(analysis);
                log.info("新增薄弱分类分析（一级） - userId: {}, category: {}, isWeak: {}", userId, categoryName, isWeak);
            } else {
                // 更新
                analysis.setWmaModuleName(categoryName);
                analysis.setWmaTotalCount(totalCount);
                analysis.setWmaCorrectCount((int) correctCount);
                analysis.setWmaCorrectRate(correctRate);
                analysis.setWmaLast3Total(last3Total);
                analysis.setWmaLast3Correct((int) last3Correct);
                analysis.setWmaLast3Rate(last3Rate);
                analysis.setWmaIsWeak(isWeak);
                analysis.setWmaUpdateTime(new Date());
                analysis.setUpdateTime(new Date());
                analysis.setUpdateUid(userId.toString());

                weakModuleAnalysisMapper.updateById(analysis);
                log.info("更新薄弱分类分析（一级） - userId: {}, category: {}, isWeak: {}", userId, categoryName, isWeak);
            }
        }

        // 7. 处理"未分类"的题目
        List<DailyTaskDetail> uncategorizedRecords = recordsByLevel1Category.getOrDefault(null, new ArrayList<>());
        if (!uncategorizedRecords.isEmpty()) {
            String uncategorizedId = null;
            String uncategorizedName = "未分类";

            int totalCount = uncategorizedRecords.size();
            long correctCount = uncategorizedRecords.stream()
                    .filter(detail -> Boolean.TRUE.equals(detail.getDtdIsCorrect()))
                    .count();
            BigDecimal correctRate = calculateRate(correctCount, totalCount);

            List<DailyTaskDetail> last3Records = uncategorizedRecords.stream()
                    .limit(3)
                    .collect(Collectors.toList());

            int last3Total = last3Records.size();
            long last3Correct = last3Records.stream()
                    .filter(detail -> Boolean.TRUE.equals(detail.getDtdIsCorrect()))
                    .count();
            BigDecimal last3Rate = calculateRate(last3Correct, last3Total);

            boolean isWeak = last3Rate.compareTo(WEAK_THRESHOLD) < 0;

            WeakModuleAnalysis analysis = existingMap.get(uncategorizedId);
            if (analysis == null) {
                analysis = new WeakModuleAnalysis();
                analysis.setWmaId(IdUtil.getSnowflakeNextIdStr());
                analysis.setWmaUserId(userId);
                analysis.setWmaModuleId(uncategorizedId);
                analysis.setWmaModuleName(uncategorizedName);
                analysis.setWmaTotalCount(totalCount);
                analysis.setWmaCorrectCount((int) correctCount);
                analysis.setWmaCorrectRate(correctRate);
                analysis.setWmaLast3Total(last3Total);
                analysis.setWmaLast3Correct((int) last3Correct);
                analysis.setWmaLast3Rate(last3Rate);
                analysis.setWmaIsWeak(isWeak);
                analysis.setWmaUpdateTime(new Date());
                analysis.setDelStatus(0);
                analysis.setCreateTime(new Date());
                analysis.setCreateUid(userId.toString());

                weakModuleAnalysisMapper.insert(analysis);
                log.info("新增薄弱分类分析 - userId: {}, category: {}, isWeak: {}", userId, uncategorizedName, isWeak);
            } else {
                analysis.setWmaModuleName(uncategorizedName);
                analysis.setWmaTotalCount(totalCount);
                analysis.setWmaCorrectCount((int) correctCount);
                analysis.setWmaCorrectRate(correctRate);
                analysis.setWmaLast3Total(last3Total);
                analysis.setWmaLast3Correct((int) last3Correct);
                analysis.setWmaLast3Rate(last3Rate);
                analysis.setWmaIsWeak(isWeak);
                analysis.setWmaUpdateTime(new Date());
                analysis.setUpdateTime(new Date());
                analysis.setUpdateUid(userId.toString());

                weakModuleAnalysisMapper.updateById(analysis);
                log.info("更新薄弱分类分析 - userId: {}, category: {}, isWeak: {}", userId, uncategorizedName, isWeak);
            }
        }

        log.info("用户薄弱分类分析更新完成 - userId: {}", userId);
    }

    @Override
    public List<WeakModuleAnalysis> getUserWeakModules(Long userId) {
        List<WeakModuleAnalysis> allAnalysis = weakModuleAnalysisMapper.selectWeakModulesByUserId(userId);

        // 只返回薄弱模块
        return allAnalysis.stream()
                .filter(WeakModuleAnalysis::getWmaIsWeak)
                .collect(Collectors.toList());
    }

    @Override
    public List<WeakModuleAnalysis> getAllModuleAnalysis(Long userId) {
        return weakModuleAnalysisMapper.selectWeakModulesByUserId(userId);
    }

    /**
     * 按一级分类分组答题记录
     * <p>
     * 1. 从DailyTaskDetail中获取二级分类ID（dtdModuleId，即qc_id）
     * 2. 通过qc_id查询edu_cs_ques_cat获取二级分类名称（qc_name）
     * 3. 通过qc_name在base_check_category中查找对应的二级分类（level=2, name=qc_name）
     * 4. 获取该二级分类的parent_id，即为一级分类ID
     * 5. 按一级分类ID分组
     * 6. 如果找不到对应的一级分类，则归入null（未分类）
     * </p>
     */
    private Map<String, List<DailyTaskDetail>> groupRecordsByLevel1Category(List<DailyTaskDetail> details) {
        Map<String, List<DailyTaskDetail>> result = new HashMap<>();

        // 缓存二级分类名称到一级分类ID的映射
        Map<String, String> level2NameToLevel1IdCache = new HashMap<>();

        for (DailyTaskDetail detail : details) {
            String qcId = detail.getDtdModuleId(); // 二级分类ID（edu_cs_ques_cat.qc_id）

            if (qcId == null || qcId.trim().isEmpty()) {
                // 未分类
                result.computeIfAbsent(null, k -> new ArrayList<>()).add(detail);
                continue;
            }

            // 查询二级分类名称
            LambdaQueryWrapper<QuesCat> catWrapper = new LambdaQueryWrapper<>();
            catWrapper.eq(QuesCat::getQcId, qcId);
            catWrapper.eq(QuesCat::getDelStatus, 0);
            QuesCat quesCat = quesCatService.getOne(catWrapper);

            if (quesCat == null || quesCat.getQcName() == null) {
                // 找不到二级分类，归入未分类
                result.computeIfAbsent(null, k -> new ArrayList<>()).add(detail);
                continue;
            }

            String qcName = quesCat.getQcName();

            // 检查缓存
            String level1CategoryId;
            if (level2NameToLevel1IdCache.containsKey(qcName)) {
                level1CategoryId = level2NameToLevel1IdCache.get(qcName);
            } else {
                // 通过二级分类名称查找对应的一级分类
                com.sundot.airport.system.domain.BaseCheckCategory level1Category =
                        baseCheckCategoryService.selectLevel1ByLevel2Name(qcName);

                if (level1Category != null) {
                    level1CategoryId = String.valueOf(level1Category.getId());
                    level2NameToLevel1IdCache.put(qcName, level1CategoryId);
                } else {
                    // 找不到一级分类，归入未分类
                    level1CategoryId = null;
                    level2NameToLevel1IdCache.put(qcName, null);
                }
            }

            // 按一级分类ID分组
            result.computeIfAbsent(level1CategoryId, k -> new ArrayList<>()).add(detail);
        }

        return result;
    }

    /**
     * 计算正确率
     */
    private BigDecimal calculateRate(long correct, int total) {
        if (total == 0) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(correct)
                .divide(BigDecimal.valueOf(total), 4, RoundingMode.HALF_UP);
    }
}
