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.dto.TeachDTO;
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.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.Component;
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>
 * 当课程进入“模型提问超时”状态时执行该动作。主要功能包括：
 * - 构造与大语言模型交互所需的消息上下文；
 * - 调用大语言模型服务发起聊天请求；
 * - 对模型响应进行流式处理，并通过音频通道输出结果。
 * </p>
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ModelAskFinishTimeOutAction extends BasePromptService implements Action<LessonStateEnum, LessonStateEvent> {

    private final ChatStreamProcessor chatStreamProcessor;

    private final ChatLogsService chatLogsService;

    private final LlmSseService llmSseService;

    /**
     * 获取当前模块标识符。
     *
     * @return 返回固定字符串"lessonSummary"表示此动作为课堂小结相关操作。
     */
    @Override
    public String getModule() {
        return "lessonSummary";
    }

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

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

    /**
     * 执行状态机中的动作逻辑。
     * <p>
     * 主要流程如下：
     * 1. 将课程上下文存入线程本地变量以供后续使用；
     * 2. 构造用于与大语言模型通信的消息列表；
     * 3. 生成本次对话的唯一会话ID；
     * 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);
        TeachDTO teachDTO = ctx.getTeachDTO();
        // 构造与大模型交互的消息列表
        List<Message> messages = getPrompt(userId);
        // 生成本次会话唯一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(teachDTO.getDevice() + sessionId);

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

    /**
     * 根据用户ID构造与大语言模型交互所需的完整消息列表。
     * <p>
     * 包含以下内容：
     * 1. 来自提示模板的基础系统和用户消息；
     * 2. 近15分钟内的历史聊天记录（交替排列模型回答与用户问题）。
     * </p>
     *
     * @param userId 用户ID，用于查询相关的聊天日志。
     * @return 完整的消息列表，按顺序包含系统、用户初始提示以及历史对话记录。
     */
    private List<Message> getPrompt(Integer userId) {
        // 获取基础提示模板
        Map<String, List<Text>> prompts = this.generatePromptMessages();
        // 分别提取系统和用户的初始提示部分
        List<Text> system = prompts.get(MessageEnums.SYSTEM.getMessage());
        List<Text> user = prompts.get(MessageEnums.USER.getMessage());
        // 组装成完整的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;
    }

}
