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

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.iot.api.UserDeviceServiceApi;
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.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;

/**
 * 课程状态机中用于处理“打断讲课”事件的动作类。
 * 当学生在上课过程中提出问题时，该动作将被触发，负责构建与大语言模型交互的上下文，
 * 并通过 SSE 流式接口获取回答后推送给对应设备。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LectureStopUserAskAction extends BasePromptService implements Action<LessonStateEnum, LessonStateEvent> {

    private final UserQuestionTextCache userQuestionTextCache;

    private final UserDeviceServiceApi userDeviceServiceApi;

    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;
    }

    /**
     * 执行打断讲课逻辑。主要流程包括：
     * 1. 提取课程上下文和用户ID；
     * 2. 缓存用户提问内容；
     * 3. 查询用户绑定设备信息；
     * 4. 构造与大语言模型交互的消息列表；
     * 5. 调用大模型服务发起异步聊天请求；
     * 6. 处理响应流并推送至指定音频通道。
     *
     * @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 = userDeviceServiceApi.getDeviceByUserId(userId);


        // 构造与大模型交互的消息列表
        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);
    }

    /**
     * 构造与大语言模型交互所需的消息列表。
     * 包括系统提示、用户问题以及最近一段时间内的历史对话记录。
     *
     * @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;
    }
}
