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

import com.seer.teach.common.cache.LlmAudioPublisher;
import com.seer.teach.common.cache.LlmAudioPublisherService;
import com.seer.teach.common.dto.mq.LlmChatDTO;
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.dto.TeachAudios;
import com.seer.teach.teacher.common.utils.TeacherUtils;
import com.seer.teach.teacher.convert.TeachConvert;
import com.seer.teach.teacher.mq.ImMqProducer;
import com.seer.teach.teacher.service.TeachService;
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 java.util.List;

/**
 * 课程状态机中“讲授停止超时”动作处理类。
 * <p>
 * 当课程进入讲授阶段后，在一定时间内未收到继续指令时触发该动作，
 * 主要用于推送相关音频数据到客户端，并通知其使用的频道名称。
 * </p>
 *
 * 实现了 Spring StateMachine 的 {@link Action} 接口，作用于
 * {@link LessonStateEnum} 和 {@link LessonStateEvent} 类型的状态转换过程中。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LectureStopFinishTimeOutAction implements Action<LessonStateEnum, LessonStateEvent> {

    private final LlmAudioPublisherService llmAudioPublisherService;

    private final UserDeviceServiceApi userDeviceServiceApi;

    private final ImMqProducer imMqProducer;

    private final TeachService teachService;

    /**
     * 执行讲授停止后的超时逻辑。
     * <p>
     * 包括获取当前课程上下文、查询绑定设备、查找对应音频资源、构建频道名称并推送音频消息。
     * </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());
        // 获取用户绑定的设备标识
        String device = userDeviceServiceApi.getDeviceByUserId(userId);
        // 获取教材ID和音频排序字段
        Integer textId = ctx.getTeachDTO().getTextId();
        int sort = ctx.getAudioSort();
        // 查询与教材及排序匹配的所有音频资源
        List<TeachAudios> audios = teachService.getAudiosByTextIdAndSort(textId, sort);
        // 根据设备、上下文等信息生成唯一会话ID
        Integer sessionId = TeacherUtils.generateSessionId(device, ctx, audios, userId);
        // 构建并发送频道名称给客户端
        String channelName = getAndSendChannelName(device, sessionId);
        // 推送音频数据到指定频道
        pushAudioData(userId, audios, sessionId, channelName);
    }

    /**
     * 将音频数据推送到指定设备频道。
     * <p>
     * 每个音频项都会被转换成 {@link LlmChatDTO} 对象并通过服务发布至目标频道。
     * </p>
     *
     * @param userId      用户ID
     * @param audios      待推送的音频列表
     * @param sessionId   本次交互的会话ID
     * @param channelName 目标推送频道名称
     */
    private void pushAudioData(Integer userId, List<TeachAudios> audios, Integer sessionId, String channelName) {
        // 遍历所有音频文件并逐个推送至对应设备频道
        audios.forEach(audio -> {
            // 转换为LlmChatDTO对象
            LlmChatDTO result = TeachConvert.INSTANCE.toLlmChatDTO(sessionId, audio, System.currentTimeMillis());
            // 发布到设备对应的频道中
            llmAudioPublisherService.publishToDevice(channelName, result);
        });

        log.info("用户[{}],音频数据推送完毕 数量：[{}]", userId, audios.size());
    }

    /**
     * 构造频道名称并向客户端发送该频道名。
     * <p>
     * 使用设备ID和会话ID组合生成唯一的频道标识符，并通过 IM MQ 告知客户端使用此频道接收后续音频流。
     * </p>
     *
     * @param device    设备标识符
     * @param sessionId 会话ID
     * @return 构建完成的频道名称字符串
     */
    private String getAndSendChannelName(String device, Integer sessionId) {
        // 构建频道名称
        String channelName = LlmAudioPublisher.getChannel(device + sessionId);
        // 通过MQ告知客户端使用的频道名
        imMqProducer.sendAudioChannelName(device, channelName);
        return channelName;
    }

}
