package com.seer.teach.teacher.stateMachine.action.modelAsk;

import com.seer.teach.common.cache.LlmAudioPublisher;
import com.seer.teach.common.context.PromptContextHolder;
import com.seer.teach.common.dto.mq.LlmChatDTO;
import com.seer.teach.common.enums.MessageEnums;
import com.seer.teach.common.enums.teacher.LessonStateEnum;
import com.seer.teach.common.enums.teacher.LessonStateEvent;
import com.seer.teach.teacher.common.dto.LessonContextDTO;
import com.seer.teach.teacher.common.llm.sse.req.LlmSseChatReq;
import com.seer.teach.teacher.common.llm.sse.req.Message;
import com.seer.teach.teacher.common.llm.sse.req.Text;
import com.seer.teach.teacher.common.utils.TeacherUtils;
import com.seer.teach.teacher.common.utils.UserQuestionTextCache;
import com.seer.teach.teacher.module.entity.ChatLogsEntity;
import com.seer.teach.teacher.processor.chat.ChatStreamProcessor;
import com.seer.teach.teacher.service.ChatLogsService;
import com.seer.teach.teacher.service.LlmSseService;
import com.seer.teach.teacher.service.prompt.impl.BasePromptService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 模型提问状态动作实现类。
 * <p>
 * 当课程状态机进入“模型提问”状态时执行该动作。主要功能包括：
 * - 获取用户中断课堂提出的问题；
 * - 构造与大语言模型交互所需的消息上下文；
 * - 调用大语言模型服务获取回答；
 * - 将结果通过 SSE 流式传输给前端，并支持语音播放。
 * </p>
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ModelAskUserAskStateAction extends BasePromptService implements Action<LessonStateEnum, LessonStateEvent> {

    private final UserQuestionTextCache userQuestionTextCache;

    private final ChatStreamProcessor chatStreamProcessor;

    private final ChatLogsService chatLogsService;

    private final LlmSseService llmSseService;

    /**
     * 获取当前模块标识符。
     *
     * @return 返回固定字符串 "interruptLecture" 表示此模块用于处理课堂中断逻辑。
     */
    @Override
    public String getModule() {
        return "interruptLecture";
    }

    /**
     * 获取当前功能名称描述。
     *
     * @return 返回中文描述 "打断上课"。
     */
    @Override
    public String getFunctionName() {
        return "打断上课";
    }

    /**
     * 获取角色 ID。
     *
     * @return 固定返回整数 7，表示特定的角色身份。
     */
    @Override
    public Integer getRoleId() {
        return 7;
    }

    /**
     * 执行状态转换时的具体业务逻辑。
     * <p>
     * 主要流程如下：
     * 1. 提取课程上下文和用户ID；
     * 2. 缓存中取出用户的提问内容；
     * 3. 查询用户绑定设备信息；
     * 4. 构造发送给大语言模型的消息列表；
     * 5. 发起异步聊天请求；
     * 6. 处理响应流并推送到客户端。
     * </p>
     *
     * @param stateContext 状态机上下文对象，包含当前状态、事件及扩展数据等信息。
     */
    @Override
    public void execute(StateContext<LessonStateEnum, LessonStateEvent> stateContext) {
        // 提取课程上下文和用户ID
        LessonContextDTO ctx = stateContext.getExtendedState().get("ctx", LessonContextDTO.class);
        Integer userId = Integer.valueOf(stateContext.getStateMachine().getId());
        // 将课程上下文放入线程本地变量中供后续使用
        PromptContextHolder.put(PromptContextHolder.LESSON, ctx);

        // 获取缓存中的用户提问文本
        String userQuestion = userQuestionTextCache.poll(userId);
        log.info("用户[{}],打断讲课 提问：[{}]", userId, userQuestion);

        // 根据用户ID查询其绑定设备信息
        String device = ctx.getTeachDTO().getDevice();

        // 构造与大模型交互的消息列表
        List<Message> messages = getPrompt(userId, userQuestion);

        // 生成本次会话唯一ID
        Integer sessionId = TeacherUtils.generateSessionId(userId, ctx, messages);

        // 创建向大模型发送的聊天请求对象
        LlmSseChatReq llmReq = new LlmSseChatReq(sessionId, userId, messages);

        // 发起异步聊天请求并获得响应数据流
        Flux<LlmChatDTO> flux = llmSseService.aiChat(llmReq);

        // 构建音频发布通道名称
        String channelName = LlmAudioPublisher.getChannel(device + sessionId);

        // 使用聊天处理器对结果进行流式处理和输出
        chatStreamProcessor.process(userId, llmReq, device, flux, channelName);
    }

    /**
     * 构造发送给大语言模型的消息列表。
     * <p>
     * 包括以下几部分内容：
     * 1. 基础提示词（系统+用户）；
     * 2. 用户的实际问题；
     * 3. 最近一段时间内的历史对话记录。
     * </p>
     *
     * @param userId       用户ID
     * @param userQuestion 用户提出的问题文本
     * @return 完整的消息列表，可用于调用大语言模型接口
     */
    private List<Message> getPrompt(Integer userId, String userQuestion) {
        // 获取基础提示模板
        Map<String, List<Text>> prompts = this.generatePromptMessages();
        // 分别提取系统和用户的初始提示部分
        List<Text> system = prompts.get(MessageEnums.SYSTEM.getMessage());
        List<Text> user = prompts.get(MessageEnums.USER.getMessage());
        // 添加实际的学生问题到用户消息中
        List<Text> text = Text.text(userQuestion);
        user.addAll(text);
        // 组装成完整的Message对象
        Message systemMessage = Message.system(system);
        Message userMessage = Message.user(user);
        // 合并为最终的消息列表
        List<Message> messages = new ArrayList<>();
        messages.add(systemMessage);
        messages.add(userMessage);
        List<ChatLogsEntity> logs = chatLogsService.getChatLogIn15minutes(userId);
        List<Message> collect = logs.stream()
                .flatMap(log -> Stream.of(
                        Message.assistant(Text.text(log.getModelAnswer())),
                        Message.user(Text.text(log.getUserQuestion()))
                ))
                .collect(Collectors.toList());
        messages.addAll(collect);
        return messages;
    }

}
