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

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.seer.teach.admin.convert.BankQuestionsConvert;
import com.seer.teach.admin.request.BankQuestionsBatchSaveReq;
import com.seer.teach.admin.request.GenerateTaskReq;
import com.seer.teach.admin.response.BankOptionsResp;
import com.seer.teach.admin.response.BankQuestionsResp;
import com.seer.teach.admin.service.AdminBankQuestionsService;
import com.seer.teach.admin.service.BankQuestionGenerationService;
import com.seer.teach.admin.util.PromptBuilder;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.teacher.module.entity.BankQuestionsEntity;
import com.seer.teach.teacher.module.entity.KnowledgePointEntity;
import com.seer.teach.teacher.module.entity.MaterialEntity;
import com.seer.teach.teacher.module.param.KnowledgePointSearchParam;
import com.seer.teach.teacher.service.BankQuestionsService;
import com.seer.teach.teacher.service.KnowledgePointQueryService;
import com.seer.teach.teacher.service.QuestionGenerationLogService;
import com.seer.teach.teacher.service.platform.LlmResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 题目生成服务实现类
 * </p>
 *
 * @author ZS
 * @since 2025-10-31
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BankQuestionGenerationServiceImpl extends AbstractGenerationService<BankQuestionsResp, GenerateTaskReq, BankQuestionsEntity> implements BankQuestionGenerationService {

    private final KnowledgePointQueryService knowledgePointQueryService;

    private final BankQuestionsService bankQuestionsService;

    private final AdminBankQuestionsService adminBankQuestionsService;

    private final QuestionGenerationLogService questionGenerationLogService;

    /**
     * 根据知识点生成题库题目
     *
     * @param req 生成题目参数
     * @return 生成的题目列表
     */
    @Override
    public List<BankQuestionsResp> generateBankQuestions(GenerateTaskReq req) {
        log.info("开始根据示例题目生成题库题目，请求参数: {}", req);

        // 1. 根据示例题目id查询对应的知识点和示例题目
        Integer knowId = req.getKnowId();
        KnowledgePointEntity knowledgePoints = knowledgePointQueryService.getKnowledgePointByKnowId(knowId);
        AssertUtils.notNull(knowledgePoints, ResultCodeEnum.KNOW_NOT_EXIST);
        req.setGradeType(knowledgePoints.getGradeType());
        req.setSubjectId(knowledgePoints.getSubjectId());
        if (StringUtils.isEmpty(knowledgePoints.getSummary())) {
            log.error("知识点[{}]下没有知识点摘要，请先添加知识点摘要", knowId);
            return Collections.emptyList();
        }
        req.setSummary(knowledgePoints.getSummary());
        if (CollectionUtil.isEmpty(req.getGradeIds())) {
            Collection<MaterialEntity> materials = knowledgePointQueryService.getMaterialListByKnowId(knowId);
            if (CollectionUtil.isEmpty(materials)) {
                log.warn("知识点下没有教材，请先添加教材");
                return Collections.emptyList();
            }
            req.setGradeIds(materials.stream().map(MaterialEntity::getGradeId).collect(Collectors.toSet()));
        }
        // 2. 调用大模型生成题目
        List<BankQuestionsResp> generatedQuestions = callLLMToGenerateBankQuestions(knowledgePoints, req);

        log.info("生成题库题目完成，共生成 {} 道题目", generatedQuestions.size());
        return generatedQuestions;
    }

    @Override
    public Integer generateAndSaveAllBankQuestions(GenerateTaskReq req) {
        Integer count = req.getCount();
        Collection<Integer> knowIds = new HashSet<>();
        if (Objects.nonNull(req.getSubjectId()) || Objects.nonNull(req.getVersionId()) || Objects.nonNull(req.getGradeId())) {
            KnowledgePointSearchParam knowledgePointSearchParam = BankQuestionsConvert.INSTANCE.generateBankQuestionReq2KnowledgePointSearchParam(req);
            log.info("根据条件查询知识点，参数: {}", knowledgePointSearchParam);
            Collection<Integer> queryKnowIds = knowledgePointQueryService.getKnowIdsByConditions(knowledgePointSearchParam);
            if (CollectionUtil.isEmpty(queryKnowIds)) {
                return 0;
            }
            knowIds.addAll(queryKnowIds);
        }
        Collection<KnowledgePointEntity> knowledgePoints = knowledgePointQueryService.getKnowledgePointListByIdsOrElseAll(knowIds);
        int totalCount = 0;
        for (KnowledgePointEntity knowledgePoint : knowledgePoints) {
            String taskId = getTaskIdFromRequest(req);
            if (isThreadInterrupted() || isTaskCancelled(taskId)) {
                log.info("任务被中断或取消，停止执行");
                return totalCount;
            }
            long counted = bankQuestionsService.count(new LambdaQueryWrapper<>(BankQuestionsEntity.class)
                    .eq(BankQuestionsEntity::getKnowId, knowledgePoint.getId()));
            if (counted >= count) {
                log.info("知识点: {} 已存在的题目总数: {}，要求的题目数量：{},已经超过，跳过该知识点", knowledgePoint.getKnowledgePointName(), counted, count);
                continue;
            }
            log.info("知识点: {} 已生成题目数量: {}，开始生成题目", knowledgePoint.getKnowledgePointName(), counted);
            int remainingCount = count - Math.toIntExact(counted);
            int actualCount = remainingCount > 4 ? remainingCount / 4 : 1;
            req.setCount(actualCount);
            req.setKnowId(knowledgePoint.getId());
            totalCount += executeBatchProcess(
                    req,
                    this::generateBankQuestionsWrapper,
                    this::saveGeneratedBankQuestions
            );
        }
        log.info("生成题库题目任务完成，生成的数量：{}",totalCount);
        return totalCount;
    }

    /**
     * 包装generateBankQuestions方法以适配函数式接口
     *
     * @param req 生成题目请求
     * @return 题目列表
     */
    private List<BankQuestionsResp> generateBankQuestionsWrapper(GenerateTaskReq req) {
        return this.generateBankQuestions(req);
    }

    /**
     * 保存生成的题库题目
     *
     * @param questions 生成的题目列表
     */
    private void saveGeneratedBankQuestions(List<BankQuestionsResp> questions) {
        if (questions.isEmpty()) {
            return;
        }
        BankQuestionsBatchSaveReq batchSaveReq = new BankQuestionsBatchSaveReq();
        // 转换题目列表
        List<BankQuestionsBatchSaveReq.BankQuestionItem> questionItems = new ArrayList<>();
        for (BankQuestionsResp question : questions) {
            if (StringUtils.isBlank(question.getAnswer())) {
                log.info("题目无答案：{}", question.getTitle());
                continue;
            }
            if (Objects.isNull(question.getType())) {
                log.info("题目无题型：{}", question.getTitle());
                continue;
            }

            BankQuestionsBatchSaveReq.BankQuestionItem item = new BankQuestionsBatchSaveReq.BankQuestionItem();
            item.setTitle(question.getTitle());
            item.setAnswer(question.getAnswer());
            item.setSolution(question.getSolution());
            item.setType(question.getType());
            item.setIsNeedImage(question.getIsNeedImage());
            item.setDifficulty(question.getDifficulty());
            // 处理选项
            if (question.getOptions() != null) {
                List<BankOptionsResp> optionItems = new ArrayList<>();
                question.getOptions().forEach(option -> {
                    BankOptionsResp optionItem = new BankOptionsResp();
                    optionItem.setContent(option.getContent());
                    optionItem.setSort(option.getSort());
                    optionItems.add(optionItem);
                });
                item.setOptions(optionItems);
            }
            questionItems.add(item);
        }
        batchSaveReq.setQuestions(questionItems);
        // 保存到数据库
        adminBankQuestionsService.batchSaveGeneratedBankQuestions(batchSaveReq);

    }

    /**
     * 调用大模型生成题库题目
     *
     * @param knowledgePoints 知识点
     * @param req             生成题目参数
     * @return 生成的题目列表
     */
    private List<BankQuestionsResp> callLLMToGenerateBankQuestions(KnowledgePointEntity knowledgePoints, GenerateTaskReq req) {
        Integer count = req.getCount();
        // 构建提示词
        String prompt = PromptBuilder.buildPromptForBankQuestionGeneration(knowledgePoints, count, req);

        log.info("调用大模型生成题库题目，提示词长度: {}", prompt.length());

        // 调用大模型API生成题目
        Map<String, Object> params = new HashMap<>();
        if (Objects.nonNull(req.getTaskId())) {
            params.putIfAbsent("taskId", req.getTaskId());
        }
        LlmResponse result = callLLM(req.getScenarioCode(), prompt, params, req.getUserId());
        if (Objects.isNull(result)) {
            log.warn("调用大模型生成题库题目失败，返回结果为空");
            return Collections.emptyList();
        }

        // 使用新的泛型方法解析响应
        List<BankQuestionsResp> questions = new ArrayList<>();
        for (LlmResponse.Choice choice : result.getChoices()) {
            LlmResponse.Message message = choice.getMessage();
            if (Objects.isNull(message)) {
                log.warn("调用大模型生成题库题目失败，返回结果中 'message' 为空");
                continue;
            }
            String content = message.getContent();
            List<BankQuestionsResp> resultList = parseLLMResponse(content, questionNode -> {
                BankQuestionsResp bankResp = new BankQuestionsResp();
                // 解析基本字段
                bankResp.setTitle(getStringValue(questionNode, "title"));
                bankResp.setAnswer(parseAnswer(questionNode.has("answer") ? questionNode.get("answer") : null));
                bankResp.setSolution(getStringValue(questionNode, "solution"));

                if (questionNode.has("type")) {
                    bankResp.setType(questionNode.get("type").asInt());
                }
                if (questionNode.has("isNeedImage")) {
                    bankResp.setIsNeedImage(questionNode.get("isNeedImage").asInt());
                }
                if (questionNode.has("difficulty")) {
                    bankResp.setDifficulty(new BigDecimal(questionNode.get("difficulty").asText()));
                }
                // 解析选项（如果有）
                if (questionNode.has("options") && questionNode.get("options").isArray()) {
                    List<BankOptionsResp> optionsResp = new ArrayList<>();
                    JsonNode optionsArray = questionNode.get("options");

                    for (JsonNode optionNode : optionsArray) {
                        BankOptionsResp optionResp = new BankOptionsResp();
                        optionResp.setContent(getStringValue(optionNode, "content"));
                        optionResp.setSort(getIntValue(optionNode, "sort"));
                        optionsResp.add(optionResp);
                    }
                    bankResp.setOptions(optionsResp);
                }
                if (questionNode.has("html")) {
                    bankResp.setHtml(getStringValue(questionNode, "html"));
                }
                if (questionNode.has("questionHtml")) {
                    bankResp.setQuestionHtml(getStringValue(questionNode, "questionHtml"));
                }
                return bankResp;
            });
            questions.addAll(resultList);
        }
        return questions;
    }

    /**
     * 解析答案字段，支持字符串和数组两种格式
     *
     * @param answerNode 答案节点
     * @return 处理后的答案字符串
     */
    private String parseAnswer(JsonNode answerNode) {
        if (answerNode == null || answerNode.isNull()) {
            return "";
        }

        // 如果是字符串，直接返回
        if (answerNode.isTextual()) {
            return answerNode.asText();
        }

        // 如果是数组，用逗号连接
        if (answerNode.isArray()) {
            List<String> answers = new ArrayList<>();
            for (JsonNode node : answerNode) {
                if (node.isTextual()) {
                    answers.add(node.asText());
                }
            }
            return String.join(",", answers);
        }
        return answerNode.asText();
    }

    @Override
    protected String getTaskIdFromRequest(GenerateTaskReq request) {
        return request.getTaskId();
    }
}