package com.seer.teach.teacher.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seer.teach.common.dto.mq.im.QuestionCorrectParseHtmlDTO;
import com.seer.teach.common.enums.QuestionTypesEnums;
import com.seer.teach.common.enums.teacher.TaskStatusEnum;
import com.seer.teach.teacher.common.dto.QuestionResultDTO;
import com.seer.teach.teacher.common.dto.QuestionSetDTO;
import com.seer.teach.teacher.common.llm.resp.QuestionsCorrectLlmResult;
import com.seer.teach.teacher.common.req.UpdateExamTopicPointReq;
import com.seer.teach.teacher.common.resp.DoExerciseResp;
import com.seer.teach.teacher.common.resp.QuestionResp;
import com.seer.teach.teacher.common.resp.QuestionsCorrectDTO;
import com.seer.teach.teacher.common.utils.TeacherUtils;
import com.seer.teach.teacher.convert.QuestionConvert;
import com.seer.teach.teacher.module.entity.*;
import com.seer.teach.teacher.module.mapper.BankQuestionsMapper;
import com.seer.teach.teacher.module.mapper.EvaluationQuestionsMapper;
import com.seer.teach.teacher.module.mapper.EvaluationTasksMapper;
import com.seer.teach.teacher.service.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 题库题目服务实现类。
 * 提供题库相关操作的服务接口实现，如获取已完成题目、生成测评题目、构建答题结果响应等。
 */
@Service
@RequiredArgsConstructor
public class BankQuestionsServiceImpl extends ServiceImpl<BankQuestionsMapper, BankQuestionsEntity> implements BankQuestionsService {

    private final EvaluationQuestionsMapper evaluationQuestionsMapper;

    private final LessonQuestionsService lessonQuestionsService;

    private final EvaluationTasksMapper evaluationTasksMapper;

    private final BankOptionsService bankOptionsService;

    private final LessonsService lessonsService;

    @Override
    public IPage<BankQuestionsEntity> selectPage(Integer pageNum, Integer pageSize, Integer type) {
        Page<BankQuestionsEntity> page = new Page<>(pageNum, pageSize);
        return super.baseMapper.selectPage(page, new LambdaQueryWrapper<BankQuestionsEntity>().eq(Objects.nonNull(type), BankQuestionsEntity::getType, type));
    }

    /**
     * 查询用户在指定知识点下已完成的所有题目集合（测评、复习、预习）。
     *
     * @param userId 用户ID
     * @param knowId 知识点ID
     * @return 已完成的题目集合列表，每个元素代表一个任务/记录中的题目信息
     */
    @Override
    public List<QuestionSetDTO> selectDoneQuestionIds(Integer userId, Integer knowId) {
        return Stream.of(
                        getEvaluationDownQuestionIds(userId, knowId),
                        getLessonDownQuestionIds(userId, knowId)
                )
                .filter(Objects::nonNull)
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }

    /**
     * 根据用户ID和知识点ID获取已完成课程的题目信息
     *
     * @param userId 用户ID
     * @param knowId 知识点ID
     * @return 题目集合DTO列表，包含课程相关信息和题目结果列表
     */
    private List<QuestionSetDTO> getLessonDownQuestionIds(Integer userId, Integer knowId) {
        // 查询用户指定知识点下状态为已完成(状态码2)的课程列表
        List<LessonsEntity> taskList = lessonsService.lambdaQuery()
                .eq(LessonsEntity::getUserId, userId)
                .eq(LessonsEntity::getKnowId, knowId)
                .eq(LessonsEntity::getStatus, TaskStatusEnum.FINISH_STUDY.getCode())
                .list();
        if (taskList.isEmpty()) {
            return Collections.emptyList();
        }
        // 提取课程ID列表
        List<Integer> taskIds = taskList.stream()
                .map(LessonsEntity::getId)
                .collect(Collectors.toList());
        // 查询这些课程关联的所有题目信息
        List<LessonQuestionsEntity> allQuestions = lessonQuestionsService.lambdaQuery()
                .in(LessonQuestionsEntity::getLessonId, taskIds)
                .list();
        // 按 taskId 分组，方便快速取出对应题目
        Map<Integer, List<LessonQuestionsEntity>> questionsMap = allQuestions.stream()
                .collect(Collectors.groupingBy(LessonQuestionsEntity::getLessonId));
        // 构造结果 DTO 列表
        return taskList.stream().map(task -> {
            List<QuestionResultDTO> questionResults = questionsMap
                    .getOrDefault(task.getId(), Collections.emptyList())
                    .stream()
                    .map(q -> QuestionConvert.INSTANCE.toQuestionResultDTO(q.getId(), q.getResult()))
                    .collect(Collectors.toList());
            return QuestionConvert.INSTANCE.toQuestionSetDTO(
                    task.getKnowId(),
                    task.getQuestionUseTime(),
                    task.getAverageDifficulty(),
                    questionResults
            );
        }).collect(Collectors.toList());
    }


    /**
     * 获取用户在指定知识点下的测评任务中已完成的题目集合。
     *
     * @param userId 用户ID
     * @param id     测评任务ID
     * @return 测评任务对应的题目集合列表
     */
    private List<QuestionSetDTO> getEvaluationDownQuestionIds(Integer userId, Integer id) {
        List<EvaluationTasksEntity> taskList = evaluationTasksMapper.selectList(
                new LambdaQueryWrapper<EvaluationTasksEntity>()
                        .eq(EvaluationTasksEntity::getUserId, userId)
                        .eq(EvaluationTasksEntity::getKnowId, id)
                        .eq(EvaluationTasksEntity::getStatus, TaskStatusEnum.FINISH_STUDY.getCode())
        );
        if (taskList.isEmpty()) {
            return Collections.emptyList();
        }
        List<Integer> taskIds = taskList.stream()
                .map(EvaluationTasksEntity::getId)
                .collect(Collectors.toList());
        List<EvaluationQuestionsEntity> allQuestions = evaluationQuestionsMapper.selectList(
                new LambdaQueryWrapper<EvaluationQuestionsEntity>()
                        .in(EvaluationQuestionsEntity::getTaskId, taskIds)
        );
        // 按 taskId 分组，方便快速取出对应题目
        Map<Integer, List<EvaluationQuestionsEntity>> questionsMap = allQuestions.stream()
                .collect(Collectors.groupingBy(EvaluationQuestionsEntity::getTaskId));
        // 构造结果 DTO 列表
        return taskList.stream().map(task -> {
            List<QuestionResultDTO> questionResults = questionsMap
                    .getOrDefault(task.getId(), Collections.emptyList())
                    .stream()
                    .map(q -> QuestionConvert.INSTANCE.toQuestionResultDTO(q.getId(), q.getResult()))
                    .collect(Collectors.toList());
            return QuestionConvert.INSTANCE.toQuestionSetDTO(
                    task.getKnowId(),
                    task.getQuestionUseTime(),
                    task.getAverageDifficulty(),
                    questionResults
            );
        }).collect(Collectors.toList());
    }

    /**
     * 根据用户的做题情况动态生成测评题目。
     *
     * @param userId        用户ID
     * @param knowId        知识点ID
     * @param questionCount 要生成的题目数量
     * @return 符合条件的题目实体列表
     */
    @Override
    public List<BankQuestionsEntity> generateEvaluationQuestions(Integer userId, Integer knowId, Integer questionCount) {
        // 1. 查询用户历史整套题记录
        List<QuestionSetDTO> doneSets = selectDoneQuestionIds(userId, knowId);

        // 默认难度范围
        BigDecimal minDifficulty = BigDecimal.valueOf(2.0);
        BigDecimal maxDifficulty = BigDecimal.valueOf(3.0);

        // 2. 根据最近一套题动态调整难度
        if (!doneSets.isEmpty()) {
            QuestionSetDTO lastSet = doneSets.get(doneSets.size() - 1);
            List<QuestionResultDTO> questions = Optional.ofNullable(lastSet.getQuestions())
                    .orElse(Collections.emptyList());

            long total = questions.size();
            long correctCount = questions.stream()
                    .filter(q -> q.getResult() != null && q.getResult() == 1)
                    .count();

            BigDecimal correctRate = total == 0 ? BigDecimal.ZERO :
                    BigDecimal.valueOf((double) correctCount / total);

            BigDecimal lastAvgDifficulty = Optional.ofNullable(lastSet.getAverageDifficulty())
                    .orElse(BigDecimal.valueOf(2.5));

            int useTime = Optional.ofNullable(lastSet.getUseTime()).orElse(0);

            if (correctRate.compareTo(BigDecimal.valueOf(0.6)) < 0 || useTime > 300) {
                minDifficulty = lastAvgDifficulty.subtract(BigDecimal.valueOf(1.0)).max(BigDecimal.ZERO);
                maxDifficulty = lastAvgDifficulty.subtract(BigDecimal.valueOf(0.3)).max(BigDecimal.ZERO);
            } else if (correctRate.compareTo(BigDecimal.valueOf(0.85)) > 0 && useTime < 300) {
                minDifficulty = lastAvgDifficulty.add(BigDecimal.valueOf(0.3)).min(BigDecimal.valueOf(5.0));
                maxDifficulty = lastAvgDifficulty.add(BigDecimal.valueOf(1.0)).min(BigDecimal.valueOf(5.0));
            } else {
                minDifficulty = lastAvgDifficulty.subtract(BigDecimal.valueOf(0.3)).max(BigDecimal.ZERO);
                maxDifficulty = lastAvgDifficulty.add(BigDecimal.valueOf(0.3)).min(BigDecimal.valueOf(5.0));
            }
        }

        // 3. 用户已作答过的题目 ID
        Set<Integer> doneQuestionIds = doneSets.stream()
                .flatMap(s -> Optional.ofNullable(s.getQuestions()).orElse(Collections.emptyList()).stream())
                .map(QuestionResultDTO::getQuestionId)
                .collect(Collectors.toSet());

        // 4. 先取“未做过”的题
        List<BankQuestionsEntity> candidates = baseMapper.selectList(new LambdaQueryWrapper<BankQuestionsEntity>()
                .eq(BankQuestionsEntity::getKnowId, knowId)
                .between(BankQuestionsEntity::getDifficulty, minDifficulty, maxDifficulty)
                .notIn(!doneQuestionIds.isEmpty(), BankQuestionsEntity::getId, doneQuestionIds)
                .orderByAsc(BankQuestionsEntity::getSort)
                .last("LIMIT " + questionCount)
        );

        // 5. 如果数量不足，继续补题，但保证不重复
        if (candidates.size() < questionCount) {
            int remain = questionCount - candidates.size();

            // 已经选过的题目ID（未做过 + 补题都禁止重复）
            Set<Integer> existedIds = candidates.stream()
                    .map(BankQuestionsEntity::getId)
                    .collect(Collectors.toSet());
            existedIds.addAll(doneQuestionIds);

            List<BankQuestionsEntity> supplement = baseMapper.selectList(new LambdaQueryWrapper<BankQuestionsEntity>()
                    .eq(BankQuestionsEntity::getKnowId, knowId)
                    .between(BankQuestionsEntity::getDifficulty, minDifficulty, maxDifficulty)
                    .notIn(!existedIds.isEmpty(), BankQuestionsEntity::getId, existedIds)
                    .orderByAsc(BankQuestionsEntity::getSort)
                    .last("LIMIT " + remain)
            );

            candidates.addAll(supplement);
        }

        return candidates;
    }


    /**
     * 构建批改后的答题结果响应对象。
     *
     * @param llmResult  LLM模型返回的结果数据
     * @param type       类型标识符
     * @param bigDecimal 正确率数值
     * @param recordId   记录ID
     * @param useTime    使用时间（单位：秒）
     * @return 批改结果封装对象
     */
    @Override
    public QuestionsCorrectDTO buildCorrectResult(QuestionsCorrectLlmResult llmResult, Map<Integer, Integer> idQuestionIdMaps, Integer type, BigDecimal bigDecimal, Integer recordId, Set<Integer> bankQuestionIds, int useTime) {
        List<Integer> correctIds = llmResult.getRightQuestionIds();
        List<BankQuestionsEntity> questions = this.listByIds(bankQuestionIds);
        //解析页面
        List<QuestionCorrectParseHtmlDTO> parseHtmlList = questions.stream()
                .map(q -> {
                    int id = idQuestionIdMaps.get(q.getId());
                    int result = correctIds.contains(id) ? 1 : 0;
                    return QuestionConvert.INSTANCE.toQuestionCorrectParseHtmlResp(result, q.getHtml());
                }).collect(Collectors.toList());
        //正确率
        String correctRateStr = TeacherUtils.formatAccuracyPercentage(bigDecimal);
        String useTimeWithString = TeacherUtils.formatDuration(useTime);
        return QuestionConvert.INSTANCE.toQuestionsCorrectResp(
                recordId,
                type,
                correctIds.size(),
                useTimeWithString,
                LocalDate.now(),
                correctRateStr,
                parseHtmlList
        );
    }

    /**
     * 更新题目的考点字段。
     *
     * @param req 更新请求参数，包含题目ID与新的考点内容
     */
    @Override
    public synchronized void updateExamTopicPoint(UpdateExamTopicPointReq req) {
        // 根据题目ID更新知识点字段
        update(new LambdaUpdateWrapper<BankQuestionsEntity>()
                .eq(BankQuestionsEntity::getId, req.getId())
                .set(BankQuestionsEntity::getExaminationPoint, req.getExaminationPoint()));
    }

    /**
     * 构建练习题响应对象。
     *
     * @param taskId        任务ID
     * @param questions     原始题目列表
     * @param ids           题目顺序ID列表
     * @param idQuestionMap 映射关系：题目顺序ID -> 实际题目ID
     * @param code          结果码
     * @return 练习题响应对象
     */
    @Override
    public DoExerciseResp buildDoExerciseResp(Integer taskId, List<BankQuestionsEntity> questions, List<Integer> ids, Map<Integer, Integer> idQuestionMap, Integer code) {
        Map<Integer, BankQuestionsEntity> questionMap = questions.stream()
                .collect(Collectors.toMap(BankQuestionsEntity::getId, Function.identity()));
        Set<Integer> choiceQuestionIds = questionMap.values().stream()
                .filter(q -> {
                    Integer type = q.getType();
                    return Objects.equals(type, QuestionTypesEnums.SINGLE_CHOICE_QUESTIONS.getCode()) ||
                            Objects.equals(type, QuestionTypesEnums.MULTIPLE_CHOICE_QUESTIONS.getCode());
                })
                .map(BankQuestionsEntity::getId)
                .collect(Collectors.toSet());
        Map<Integer, List<BankOptionsEntity>> optionMaps = bankOptionsService.lambdaQuery()
                .in(BankOptionsEntity::getQuestionId, choiceQuestionIds.isEmpty() ? Collections.singleton(-1) : choiceQuestionIds)
                .list()
                .stream()
                .collect(Collectors.groupingBy(BankOptionsEntity::getQuestionId));
        List<QuestionResp> questionRespList = ids.stream()
                .map(orderId -> {
                    Integer questionId = idQuestionMap.get(orderId);
                    BankQuestionsEntity question = questionMap.get(questionId);
                    List<String> options = null;
                    if (choiceQuestionIds.contains(questionId)) {
                        List<BankOptionsEntity> opts = optionMaps.get(questionId);
                        options = opts.stream()
                                .map(BankOptionsEntity::getContent)
                                .collect(Collectors.toList());
                    }
                    return new QuestionResp(orderId,
                            question.getType(),
                            question.getTitle(),
                            options);
                })
                .collect(Collectors.toList());
        return QuestionConvert.INSTANCE.toDoExerciseResp(taskId, code, questionRespList);
    }

}
