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

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>
 * 此类实现了状态机的动作接口 {@link Action}，用于处理当课程处于特定状态下，
 * 用户发起提问时的业务逻辑。包括获取用户问题、构造与大语言模型交互的消息内容、
 * 调用AI服务进行回答，并将结果通过SSE方式推送给前端。
 * </p>
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LectureUserAskAction 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 角色ID：7
     */
    @Override
    public Integer getRoleId() {
        return 7;
    }

    /**
     * 执行状态动作的核心方法。
     * <p>
     * 主要流程如下：
     * 1. 获取当前登录用户ID及课程上下文；
     * 2. 将课程上下文设置进线程本地变量以供后续使用；
     * 3. 取出缓存中的用户提问内容；
     * 4. 构造与大语言模型通信所需的消息列表；
     * 5. 生成会话ID并构建请求对象；
     * 6. 异步调用AI服务获取响应流；
     * 7. 利用聊天处理器对响应流进行处理和推送。
     * </p>
     *
     * @param stateContext 当前状态机上下文，包含扩展状态等信息
     */
    @Override
    public void execute(StateContext<LessonStateEnum, LessonStateEvent> stateContext) {
        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. 查询最近15分钟内的历史对话记录作为上下文补充；
     * 4. 合并所有消息形成最终输入给模型的内容。
     * </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;
    }
}
