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


import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.seer.teach.common.PageListBean;
import com.seer.teach.common.enums.AiScenarioCodeEnum;
import com.seer.teach.common.enums.BankQuestionStatusEnum;
import com.seer.teach.common.enums.QuestionResultEnums;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.common.utils.PageConverterUtils;
import com.seer.teach.teacher.common.req.WrongQuestionBookReq;
import com.seer.teach.teacher.common.req.WrongQuestionPageListReq;
import com.seer.teach.teacher.common.resp.SimilarQuestionResp;
import com.seer.teach.teacher.common.resp.WrongQuestionPageListResp;
import com.seer.teach.teacher.common.resp.WrongQuestionResp;
import com.seer.teach.teacher.convert.WrongQuestionBookConvert;
import com.seer.teach.teacher.module.entity.*;
import com.seer.teach.teacher.service.*;
import com.seer.teach.teacher.service.platform.LlmResponse;
import com.seer.teach.teacher.util.WrongQuestionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

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

/**
 * 错题服务实现类，提供错题列表查询、科目错题数量查询、删除错题、举一反三等功能。
 *
 * @author 嘉多宝宝
 * @since 2025-12-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WrongQuestionServiceImpl implements WrongQuestionService {

    private final static String TOTAL_WRONG_QUESTION_COUNT = "共有%d道错题";

    private final EvaluationQuestionsService evaluationQuestionsService;

    private final EvaluationTasksService evaluationTasksService;

    private final LessonQuestionsService lessonQuestionsService;

    private final BankQuestionsService bankQuestionsService;

    private final AiModelCallService aiModelCallService;

    private final SubjectsService subjectsService;

    private final LessonsService lessonsService;

    /**
     * 获取各科目的错题数量
     *
     * @param params 请求参数，包含科目ID等信息
     * @return 返回封装了总错题数的响应对象
     */
    @Override
    public WrongQuestionResp getWrongQuestionCount(WrongQuestionBookReq params) {
        Integer userId = StpUtil.getLoginIdAsInt();
        log.info("开始获取用户[{}]测评错题本数据", userId);

        // 取科目名称
        SubjectsEntity subject = subjectsService.getById(params.getSubjectId());
        AssertUtils.notNull(subject, ResultCodeEnum.SUBJECT_NOT_EXIST);

        // 获取课程及课程错题
        List<LessonsEntity> lessons = getLessons(userId, params);
        if (lessons == null) {
            lessons = Collections.emptyList();
        }
        List<LessonQuestionsEntity> lessonWrongQuestions = Collections.emptyList();
        if (!lessons.isEmpty()) {
            Set<Integer> lessonIds = lessons.stream()
                    .map(LessonsEntity::getId).filter(Objects::nonNull).collect(Collectors.toSet());
            if (!lessonIds.isEmpty()) {
                lessonWrongQuestions = getLessonWrongQuestions(lessonIds);
                if (lessonWrongQuestions == null) {
                    lessonWrongQuestions = Collections.emptyList();
                }
            }
        }

        // 查询科目测评任务及测评错题
        List<EvaluationTasksEntity> tasks = getTasksEntityList(userId, params);
        if (tasks == null) {
            tasks = Collections.emptyList();
        }
        List<EvaluationQuestionsEntity> evaluationWrongQuestions = Collections.emptyList();
        if (!tasks.isEmpty()) {
            Set<Integer> taskIds = tasks.stream().map(EvaluationTasksEntity::getId).filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            if (!taskIds.isEmpty()) {
                evaluationWrongQuestions = getQuestionsEntityList(taskIds);
                if (evaluationWrongQuestions == null) {
                    evaluationWrongQuestions = Collections.emptyList();
                }
            }
        }

        // 获取错题总数
        Integer totalCount = Integer.sum(evaluationWrongQuestions.size(), lessonWrongQuestions.size());
        String totalWrongCount = String.format(TOTAL_WRONG_QUESTION_COUNT, totalCount);
        return WrongQuestionBookConvert.INSTANCE.toWrongQuestion(totalWrongCount);
    }

    /**
     * 分页查询获取错题列表
     *
     * @param params 请求参数，包括分页信息和科目ID等
     * @return 封装了分页错题列表的数据结构
     */
    @Override
    public PageListBean<WrongQuestionPageListResp> getWrongQuestionList(WrongQuestionPageListReq params) {
        // 获取用户Id
        Integer userId = StpUtil.getLoginIdAsInt();
        log.info("开始获取用户[{}]错题列表", userId);

        // 构建分页对象
        Page<BankQuestionsEntity> page = new Page<>(params.getPage(), params.getLimits());
        Set<Integer> questionIds = new HashSet<>();
        Map<Integer, String> studentAnswerMap = new HashMap<>();

        // 查询测评错题
        List<EvaluationQuestionsEntity> evaluationQuestions = getEvaluationQuestions(params, userId);
        processQuestions(evaluationQuestions, EvaluationQuestionsEntity::getQuestionId,
                EvaluationQuestionsEntity::getStudentAnswer, questionIds, studentAnswerMap);

        // 查询课程错题
        List<LessonQuestionsEntity> lessonQuestions = getLessonQuestions(params, userId);
        processQuestions(lessonQuestions, LessonQuestionsEntity::getQuestionId,
                LessonQuestionsEntity::getStudentAnswer, questionIds, studentAnswerMap);

        // 如果没有任何错题，抛出错题不存在
        if (questionIds.isEmpty()) {
            AssertUtils.fail(ResultCodeEnum.WRONG_QUESTION_NOT_EXIST);
        }

        // 查询题目信息
        Page<BankQuestionsEntity> questions = bankQuestionsService.page(page,
                new LambdaQueryWrapper<BankQuestionsEntity>()
                        .in(BankQuestionsEntity::getId, questionIds));

        return PageConverterUtils.convertPageListBean(questions, records ->
                records.stream().map(question -> {
                    String studentAnswer = studentAnswerMap.get(question.getId());
                    return WrongQuestionUtils.addStudentAnswerToQuestion(question, studentAnswer);
                }).collect(Collectors.toList())
        );
    }

    /**
     * 删除指定题目的错题记录（支持测评或课程中的错题）
     *
     * @param params 包含要删除的题目ID的对象
     */
    @Override
    public void deleteWrongQuestion(IdEntity params) {
        Integer userId = StpUtil.getLoginIdAsInt();
        log.info("开始删除用户:[{}]错题,题目Id:[{}]", userId, params.getId());
        // 删除测评错题
        List<EvaluationTasksEntity> evaluationTasks = evaluationTasksService.lambdaQuery()
                .eq(EvaluationTasksEntity::getUserId, userId)
                .list();
        if (evaluationTasks != null && !evaluationTasks.isEmpty()) {
            List<Integer> taskIds = evaluationTasks.stream().map(EvaluationTasksEntity::getId).collect(Collectors.toList());
            evaluationQuestionsService.remove(new LambdaQueryWrapper<EvaluationQuestionsEntity>()
                    .in(EvaluationQuestionsEntity::getTaskId, taskIds)
                    .eq(EvaluationQuestionsEntity::getQuestionId, params.getId())
                    .eq(EvaluationQuestionsEntity::getResult, QuestionResultEnums.WRONG.getCode()));
        }

        // 删除做题错题
        List<LessonsEntity> lessons = lessonsService.lambdaQuery().eq(LessonsEntity::getUserId, userId).list();
        if (lessons != null && !lessons.isEmpty()) {
            List<Integer> lessonIds = lessons.stream().map(LessonsEntity::getId).collect(Collectors.toList());
            lessonQuestionsService.remove(new LambdaQueryWrapper<LessonQuestionsEntity>()
                    .in(LessonQuestionsEntity::getLessonId, lessonIds)
                    .eq(LessonQuestionsEntity::getQuestionId, params.getId())
                    .eq(LessonQuestionsEntity::getResult, QuestionResultEnums.WRONG.getCode()));
        }
    }

    /**
     * 举一反三
     *
     * @param id           题目Id
     * @param scenarioCode 场景code
     * @param count        题目数量
     * @return 题目列表
     */
    @Override
    public List<String> generateSimilarQuestions(Integer id, AiScenarioCodeEnum scenarioCode, Integer count) {
        // 获取用户Id
        Integer userId = StpUtil.getLoginIdAsInt();
        log.info("用户:{}开始生成题目,题目Id:[{}]", userId, id);
        // 查询原始错题
        BankQuestionsEntity originalQuestion = bankQuestionsService.getById(id);
        AssertUtils.notNull(originalQuestion, ResultCodeEnum.QUESTION_NOT_EXIST);
        // 获取知识点和题型
        Integer knowId = originalQuestion.getKnowId();
        Integer type = originalQuestion.getType();

        // 构建提示词
        String prompt = WrongQuestionUtils.buildPromptForSimilarQuestions(originalQuestion, knowId, type, count);
        log.info("调用大模型生成题目，提示词:\n {}", prompt);
        // 调用大模型API
        Map<String, Object> params = new HashMap<>();
        LlmResponse result = aiModelCallService.callModel(scenarioCode.getCode(), prompt, params, userId);
        if (Objects.isNull(result)) {
            log.warn("调用大模型生成题目失败,返回题目结果为空");
            return Collections.emptyList();
        }

        // 解析结果HTML字段
        return result.getChoices().stream().filter(choice -> {
            if (Objects.isNull(choice.getMessage())) {
                log.warn("调用大模型生成题目失败，返回结果中 'message' 为空");
                return false;
            }
            return true;
        }).flatMap(choice -> {
            String content = choice.getMessage().getContent();
            List<JsonNode> resultList = WrongQuestionUtils.parseLLMResponse(content);
            // 异步插入生成的题目保存到题库中
            if (!resultList.isEmpty()) {
                generateBatchInsertBankQuestions(resultList, originalQuestion);
            }
            return resultList.stream();
        }).map(questionNode -> {
            if (questionNode.has("html")) {
                return questionNode.get("html").asText();
            } else {
                return WrongQuestionUtils.constructDefaultHtml(questionNode);
            }
        }).collect(Collectors.toList());
    }

    /**
     * 举一反三获取相似题目
     *
     * @param id 题目Id
     * @return 相似题目列表
     */

    @Override
    public List<SimilarQuestionResp> getSimilarQuestionsByQuestionId(Integer id) {
        // 根据题目Id获取题目
        BankQuestionsEntity questionBank = bankQuestionsService.getById(id);
        AssertUtils.notNull(questionBank, ResultCodeEnum.QUESTION_NOT_EXIST);
        // 获取知识点Id
        Integer knowId = questionBank.getKnowId();
        // 查询相同知识点的题目，排除原题目
        List<BankQuestionsEntity> questionsList = bankQuestionsService.lambdaQuery()
                .eq(BankQuestionsEntity::getKnowId, knowId)
                .ne(BankQuestionsEntity::getId, id)
                .list();
        List<SimilarQuestionResp> similarQuestionList = WrongQuestionBookConvert.INSTANCE.toSimilarQuestionList(questionsList);
        // 过滤掉questionHtml为空的题目
        List<SimilarQuestionResp> result = similarQuestionList.stream()
                .filter(question -> question.getQuestionHtml() != null && !question.getQuestionHtml().isEmpty())
                .collect(Collectors.toList());
        // 将相似题目打乱顺序排序
        Collections.shuffle(result);
        // 返回10道知识点相同的题目
        return result.stream()
                .limit(10)
                // 重新编号(序号1-10)
                .peek((question) -> question.setId(result.indexOf(question) + 1))
                .collect(Collectors.toList());
    }

    /**
     * 异步批量插入生成的相似题目到题库
     *
     * @param resultList       解析后的题目JSON节点列表
     * @param originalQuestion 原始题目，用于获取知识点等通用信息
     */
    @Async("taskExecutor")
    public void generateBatchInsertBankQuestions(List<JsonNode> resultList, BankQuestionsEntity originalQuestion) {
        log.info("开始批量插入题目到题库");
        List<BankQuestionsEntity> result = resultList.stream().map(questionNode -> {
            BankQuestionsEntity entity = new BankQuestionsEntity();
            entity.setIsNeedImage(originalQuestion.getIsNeedImage());
            entity.setKnowId(originalQuestion.getKnowId());
            entity.setType(originalQuestion.getType());
            entity.setTitle(questionNode.get("title").asText());
            // 转换答案格式
            String rawAnswer = questionNode.get("answer").asText();
            String answer = WrongQuestionUtils.convertAnswerFormat(rawAnswer, originalQuestion.getType());
            entity.setAnswer(answer);
            entity.setSolution(questionNode.get("solution").asText());
            entity.setDifficulty(new BigDecimal(questionNode.get("difficulty").asText()));
            entity.setStatus(BankQuestionStatusEnum.START.getCode());
            entity.setIsChecked(0);
            entity.setSort(0);

            String fullHtml = "";
            if (questionNode.has("html")) {
                fullHtml = questionNode.get("html").asText();
                entity.setHtml(fullHtml);
            } else {
                fullHtml = WrongQuestionUtils.constructDefaultHtml(questionNode);
                entity.setHtml(fullHtml);
            }
            // 提取仅包含题目和选项的HTML内容
            entity.setQuestionHtml(WrongQuestionUtils.extractQuestionAndOptions(fullHtml));
            return entity;
        }).collect(Collectors.toList());

        // 批量插入题目到题库
        if (!result.isEmpty()) {
            bankQuestionsService.saveBatch(result, result.size());
            log.info("成功异步插入{}条相似题目到题库", result.size());
        } else {
            log.warn("异步批量插入相似题目失败!");
        }
    }

    /**
     * 处理问题列表，提取问题ID并收集相关数据
     *
     * @param questions           问题列表，泛型类型T表示问题对象
     * @param questionIdGetter    用于从问题对象中获取问题ID的函数
     * @param studentAnswerGetter 用于从问题对象中获取学生答案的函数
     * @param questionIds         用于存储问题ID的集合，处理后会将问题ID添加到此集合中
     * @param studentAnswerMap    用于存储问题ID与学生答案映射关系的Map
     */
    private <T> void processQuestions(List<T> questions, Function<T, Integer> questionIdGetter,
                                      Function<T, String> studentAnswerGetter,
                                      Set<Integer> questionIds,
                                      Map<Integer, String> studentAnswerMap) {
        if (questions != null && !questions.isEmpty()) {
            Set<Integer> questionId = questions.stream().map(questionIdGetter).collect(Collectors.toSet());
            questionIds.addAll(questionId);
            studentAnswerMap.putAll(questions.stream().collect(Collectors.toMap(
                    questionIdGetter,
                    studentAnswerGetter,
                    // 合并策略：取第一个
                    (k1, k2) -> k1
            )));
        }
    }

    /**
     * 根据请求参数和用户ID获取课程错题列表
     *
     * @param params 请求参数，包含科目ID等信息
     * @param userId 当前用户的ID
     * @return 课程相关的错题列表
     */
    private List<LessonQuestionsEntity> getLessonQuestions(WrongQuestionPageListReq params, Integer userId) {
        List<LessonsEntity> lessons = lessonsService.lambdaQuery()
                .eq(LessonsEntity::getSubjectId, params.getSubjectId())
                .eq(LessonsEntity::getUserId, userId)
                .list();
        if (CollectionUtil.isEmpty(lessons)) {
            return null;
        }
        List<Integer> lessIds = lessons.stream().map(LessonsEntity::getId).collect(Collectors.toList());
        return lessonQuestionsService.lambdaQuery()
                .in(LessonQuestionsEntity::getLessonId, lessIds)
                .eq(LessonQuestionsEntity::getResult, QuestionResultEnums.WRONG.getCode())
                .list();
    }

    /**
     * 根据请求参数和用户ID获取测评错题列表
     *
     * @param params 请求参数，包含科目ID等信息
     * @param userId 当前用户的ID
     * @return 测评相关的错题列表
     */
    private List<EvaluationQuestionsEntity> getEvaluationQuestions(WrongQuestionPageListReq params, Integer userId) {
        List<EvaluationTasksEntity> tasks = evaluationTasksService.lambdaQuery()
                .eq(EvaluationTasksEntity::getUserId, userId)
                .eq(EvaluationTasksEntity::getSubjectId, params.getSubjectId())
                .list();
        if (CollectionUtil.isEmpty(tasks)) {
            return null;
        }
        List<Integer> taskIds = tasks.stream().map(EvaluationTasksEntity::getId)
                .collect(Collectors.toList());
        // 获取测评错题
        return evaluationQuestionsService.lambdaQuery()
                .in(EvaluationQuestionsEntity::getTaskId, taskIds)
                .eq(EvaluationQuestionsEntity::getResult, QuestionResultEnums.WRONG.getCode())
                .list();
    }

    /**
     * 根据测评任务ID集合获取对应的错题列表
     *
     * @param taskIds 测评任务ID集合
     * @return 错题列表
     */
    private List<EvaluationQuestionsEntity> getQuestionsEntityList(Set<Integer> taskIds) {
        return evaluationQuestionsService.lambdaQuery()
                .in(EvaluationQuestionsEntity::getTaskId, taskIds)
                .eq(EvaluationQuestionsEntity::getResult, QuestionResultEnums.WRONG.getCode())
                .list();
    }

    /**
     * 根据用户ID和请求参数获取该用户在某科目下的所有测评任务
     *
     * @param userId 用户ID
     * @param params 请求参数，包含科目ID等信息
     * @return 测评任务列表
     */
    private List<EvaluationTasksEntity> getTasksEntityList(Integer userId, WrongQuestionBookReq params) {
        return evaluationTasksService.lambdaQuery()
                .eq(EvaluationTasksEntity::getUserId, userId)
                .eq(EvaluationTasksEntity::getSubjectId, params.getSubjectId())
                .list();
    }

    /**
     * 根据课程ID集合获取对应的所有课程错题
     *
     * @param lessonIds 课程ID集合
     * @return 课程错题列表
     */
    private List<LessonQuestionsEntity> getLessonWrongQuestions(Set<Integer> lessonIds) {
        return lessonQuestionsService.lambdaQuery()
                .in(LessonQuestionsEntity::getLessonId, lessonIds)
                .eq(LessonQuestionsEntity::getResult, QuestionResultEnums.WRONG.getCode())
                .list();
    }

    /**
     * 根据用户ID和请求参数获取该用户在某科目下创建的所有课程
     *
     * @param userId 用户ID
     * @param params 请求参数，包含科目ID等信息
     * @return 课程列表
     */
    private List<LessonsEntity> getLessons(Integer userId, WrongQuestionBookReq params) {
        return lessonsService.lambdaQuery()
                .eq(LessonsEntity::getUserId, userId)
                .eq(LessonsEntity::getSubjectId, params.getSubjectId())
                .list();
    }
}
