package com.qmth.boot.core.ai.service;

import com.qmth.boot.core.ai.client.LlmApiClient;
import com.qmth.boot.core.ai.model.llm.*;
import com.qmth.boot.core.ai.model.llm.score.AutoScoreModel;
import com.qmth.boot.core.ai.model.llm.score.AutoScoreRequest;
import com.qmth.boot.core.ai.model.llm.score.AutoScoreResult;
import com.qmth.boot.core.ai.model.llm.score.StandardAnswer;
import com.qmth.boot.core.retrofit.utils.SignatureInfo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * AI相关功能服务
 */
@Service
public class AiService {

    private static final Pattern score_pattern = Pattern.compile("[^\\d]*(\\d+)[^\\d]*");

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

    @Resource
    private LlmApiClient llmApiClient;

    /**
     * 获取当前机构大模型接口调用次数余额
     *
     * @param baseUrl   接口前缀地址，默认取配置文件的值（选填）
     * @param signature 使用当前机构AK作为鉴权信息
     * @param appType   大模型接口应用类型
     * @return 调用次数余额
     */
    public LlmAppBalance getLlmBalance(String baseUrl, @NotNull SignatureInfo signature, @NotNull LlmAppType appType) {
        return llmApiClient.getBalance(baseUrl, signature, appType);
    }

    /**
     * 使用自定义提示词进行自动命题
     *
     * @param baseUrl   接口前缀地址，默认取配置文件的值（选填）
     * @param signature 使用当前机构AK作为鉴权信息
     * @param request   大模型通用Chat请求
     * @return 大模型返回的文本集合
     */
    public List<String> autoGenerateQuestion(String baseUrl, @NotNull SignatureInfo signature, @NotNull ChatRequest request) {
        ChatResult result = llmApiClient.chat(baseUrl, signature, LlmAppType.AUTO_GENERATE_QUESTION, request);
        return result.getChoices().stream().filter(choice -> choice.getMessage().getRole() == ChatRole.assistant)
                .map(choice -> choice.getMessage().getContent()).collect(Collectors.toList());
    }

    /**
     * 使用预设模版进行自动命题
     *
     * @param baseUrl   接口前缀地址，默认取配置文件的值（选填）
     * @param signature 使用当前机构AK作为鉴权信息
     * @param param     基于预设模版的参数对象，可以使用Map或自定义Object
     * @return 大模型返回的文本集合
     */
    public List<String> autoGenerateQuestion(String baseUrl, @NotNull SignatureInfo signature, @NotNull Object param) {
        ChatResult result = llmApiClient.chatTemplate(baseUrl, signature, LlmAppType.AUTO_GENERATE_QUESTION, param);
        return result.getChoices().stream().filter(choice -> choice.getMessage().getRole() == ChatRole.assistant)
                .map(choice -> choice.getMessage().getContent()).collect(Collectors.toList());
    }

    /**
     * 使用预设模版进行自动判分，考生作答为文本
     *
     * @param baseUrl   接口前缀地址，默认取配置文件的值（选填）
     * @param signature 使用当前机构AK作为鉴权信息
     * @param request   自动判分请求参数
     * @return 判分结果；null表示无法获取判分结果
     */
    public AutoScoreResult autoScore(String baseUrl, @NotNull SignatureInfo signature, @NotNull @Validated AutoScoreRequest request) {
        if (StringUtils.isBlank(request.getSubjectName())) {
            throw new IllegalArgumentException("科目名称不能为空");
        }
        if (StringUtils.isBlank(request.getQuestionBody())) {
            throw new IllegalArgumentException("试题内容不能为空");
        }
        if (request.getScoreModel() == null) {
            throw new IllegalArgumentException("评分模式不能为空");
        }
        if (CollectionUtils.isEmpty(request.getStandardAnswer())) {
            throw new IllegalArgumentException("标答不能为空");
        }
        if (StringUtils.isBlank(request.getStudentAnswer())) {
            throw new IllegalArgumentException("考生回答不能为空");
        }
        if (request.getTotalScore() <= 0) {
            throw new IllegalArgumentException("试题总分必须大于0");
        }
        if (request.getIntervalScore() <= 0) {
            throw new IllegalArgumentException("最小间隔分必须大于0");
        }

        ChatResult result = llmApiClient.chatTemplate(baseUrl, signature, LlmAppType.AUTO_SCORE, request);
        String content = result.getChoices().stream().filter(choice -> choice.getMessage().getRole() == ChatRole.assistant)
                .map(choice -> choice.getMessage().getContent()).findFirst().orElse("");

        return parseScoreResult(request, content);
    }

    private AutoScoreResult parseScoreResult(AutoScoreRequest request, String content) {
        try {
            String[] values = StringUtils.split(content.replaceAll("，", ","), ",");
            int stepCount = request.getStandardAnswer().size();
            if (stepCount != values.length) {
                log.warn("评分结果无效，要求给出{}个关键分值，实际响应：{}", stepCount, content);
                return null;
            }

            double totalScore = 0;
            double[] scores = new double[stepCount];
            for (int i = 0; i < stepCount; i++) {
                // 得分率：必须 0 - 100 之间
                double stepScoreRate = Double.parseDouble(values[i].trim());
                stepScoreRate = Math.min(Math.max(stepScoreRate, 0), 100);

                StandardAnswer step = request.getStandardAnswer().get(i);
                if (AutoScoreModel.LEVEL == request.getScoreModel()) {
                    // 按档次给分模式：关键步骤得分应介于当前档次分值区间
                    double stepScore = stepScoreRate * step.getHighScore() / 100;
                    if (stepScore < step.getLowScore()) {
                        log.warn("评分结果-第{}步得分：{}，低于档次分值区间{}-{}分的最低分", i + 1, stepScore, step.getLowScore(), step.getHighScore());
                        stepScore = 0;
                    } else {
                        // 修正分值-符合最小间隔分规则
                        stepScore = this.fixScore(stepScore, request.getIntervalScore());
                    }

                    // 步骤分不能超过当前试题总分
                    scores[i] = Math.min(stepScore, request.getTotalScore());
                    totalScore = Math.max(totalScore, scores[i]);
                } else {
                    // 按得分点给分模式：关键步骤得分应小于等于当前得分点的分值
                    double stepScore = stepScoreRate * step.getScore() / 100;

                    // 修正分值-符合最小间隔分规则
                    stepScore = this.fixScore(stepScore, request.getIntervalScore());

                    // 步骤分不能超过当前试题总分
                    scores[i] = Math.min(stepScore, request.getTotalScore());
                    totalScore += scores[i];
                }
            }

            AutoScoreResult scoreResult = new AutoScoreResult();
            scoreResult.setStepScore(scores);
            scoreResult.setTotalScore(Math.min(totalScore, request.getTotalScore()));
            return scoreResult;
        } catch (Exception e) {
            log.warn("评分结果无效，实际响应：{}，错误：{}", content, e.getMessage());
            return null;
        }
    }

    /*private AutoScoreResult parseScoreResult(AutoScoreRequest request, String content) {
        try {
            String[] values = StringUtils.split(content.replaceAll("，", ","), ",");
            int stepCount = request.getStandardAnswer().size();
            if (stepCount != values.length) {
                log.warn("评分结果无效，要求给出{}个关键分值，实际响应：{}", stepCount, content);
                return null;
            }

            double totalScore = 0;
            double[] scores = new double[stepCount];
            for (int i = 0; i < stepCount; i++) {
                double stepScore = Double.parseDouble(values[i].trim());

                StandardAnswer step = request.getStandardAnswer().get(i);
                if (AutoScoreModel.LEVEL == request.getScoreModel()) {
                    // 按档次给分模式：关键步骤得分应介于当前档次分值区间
                    if (stepScore < step.getLowScore()) {
                        log.warn("评分结果-第{}步得分：{}，低于档次分值区间{}-{}分的最低分", i + 1, stepScore, step.getLowScore(), step.getHighScore());
                        stepScore = 0;
                    } else if (stepScore > step.getHighScore()) {
                        log.warn("评分结果-第{}步得分：{}，高于档次分值区间{}-{}分的最高分", i + 1, stepScore, step.getLowScore(), step.getHighScore());
                        stepScore = Math.min(stepScore, step.getHighScore());
                    } else {
                        // 修正分值-符合最小间隔分规则
                        stepScore = this.fixScore(stepScore, request.getIntervalScore());
                    }

                    // 步骤分不能超过当前试题总分
                    scores[i] = Math.min(stepScore, request.getTotalScore());
                    totalScore = Math.max(totalScore, scores[i]);
                } else {
                    // 按得分点给分模式：关键步骤得分应小于等于当前得分点的分值
                    if (stepScore > step.getScore()) {
                        log.warn("评分结果-第{}步得分：{}，大于得分点分值{}分的最大分", i + 1, stepScore, step.getScore());
                        stepScore = Math.min(stepScore, step.getScore());
                    } else {
                        // 修正分值-符合最小间隔分规则
                        stepScore = this.fixScore(stepScore, request.getIntervalScore());
                    }

                    // 步骤分不能超过当前试题总分
                    scores[i] = Math.min(stepScore, request.getTotalScore());
                    totalScore += scores[i];
                }
            }

            AutoScoreResult scoreResult = new AutoScoreResult();
            scoreResult.setStepScore(scores);
            scoreResult.setTotalScore(Math.min(totalScore, request.getTotalScore()));
            return scoreResult;
        } catch (Exception e) {
            log.warn("评分结果无效，实际响应：{}，错误：{}", content, e.getMessage());
            return null;
        }
    }*/

    /**
     * 将分数近似修正为指定“最小间隔分”的整数倍数
     *
     * @param score         分数
     * @param intervalScore 最小间隔分
     * @return 修正后的分数
     */
    private double fixScore(double score, double intervalScore) {
        if (intervalScore <= 0) {
            throw new IllegalArgumentException("最小间隔分必须大于0");
        }

        BigDecimal interval = BigDecimal.valueOf(intervalScore);
        // 计算结果 = 近似取整(score / intervalScore) * intervalScore
        return BigDecimal.valueOf(score)
                .divide(interval, 0, RoundingMode.HALF_UP)
                .multiply(interval).doubleValue();
    }

}
