package com.seer.teach.teacher.processor.chat;

import cn.hutool.json.JSONUtil;
import com.seer.teach.common.cache.LlmAudioPublisherService;
import com.seer.teach.common.dto.mq.LlmChatDTO;
import com.seer.teach.common.dto.mq.StringDTO;
import com.seer.teach.common.enums.im.ImMqTopicEnum;
import com.seer.teach.common.enums.im.RedisChannelPushTopTagEnum;
import com.seer.teach.teacher.common.llm.sse.req.LlmSseChatReq;
import com.seer.teach.teacher.common.utils.TeacherUtils;
import com.seer.teach.teacher.mq.ImMqProducer;
import com.seer.teach.teacher.processor.base.AbstractStreamProcessor;
import com.seer.teach.teacher.service.LlmSseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;


/**
 * 欢迎消息流处理器，用于处理与欢迎消息相关的 SSE 流数据。
 * <p>
 * 该类继承自 {@link AbstractStreamProcessor}，实现了针对特定业务场景（如欢迎语）的数据流处理逻辑，
 * 包括开始前操作、数据接收、错误处理及资源注册等流程。
 *
 * @author [未知]
 * @since [版本号或日期]
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class WelcomeMessagesStreamProcessor extends AbstractStreamProcessor<LlmSseChatReq, LlmChatDTO, String> {

    /**
     * 音频发布服务，负责将音频相关数据推送到指定设备通道。
     */
    private final LlmAudioPublisherService llmAudioPublisherService;

    /**
     * LLM 的 SSE 服务接口，提供流式会话能力及相关管理功能。
     */
    private final LlmSseService llmSseService;

    /**
     * IM 消息队列生产者，用于向消息中间件发送各类通知和控制信息。
     */
    private final ImMqProducer imMqProducer;

    /**
     * 在流启动之前执行的操作。
     * <p>
     * 主要作用是通过消息队列告知客户端当前使用的音频通道名称。
     *
     * @param userId         用户ID
     * @param llmSseChatReq  请求参数对象
     * @param device         设备标识
     * @param flux           数据流对象
     * @param channelName    当前分配的音频通道名
     */
    @Override
    protected void onBeforeStart(Integer userId, LlmSseChatReq llmSseChatReq, String device, Flux<LlmChatDTO> flux, String channelName) {
        imMqProducer.sendAudioChannelName(device, channelName);
    }

    /**
     * 流正式开始时调用的方法。
     * <p>
     * 此处暂无具体实现，可作为扩展点供未来使用。
     *
     * @param userId         用户ID
     * @param llmSseChatReq  请求参数对象
     * @param channelName    当前分配的音频通道名
     */
    @Override
    protected void onStart(Integer userId, LlmSseChatReq llmSseChatReq, String channelName) {

    }

    /**
     * 接收到每条数据时触发的回调方法。
     * <p>
     * 设置时间戳并把数据推送给目标设备对应的音频通道。
     *
     * @param userId      用户ID
     * @param device      设备标识
     * @param data        接收的数据对象
     * @param channelName 音频通道名称
     */
    @Override
    protected void onData(Integer userId, String device, LlmChatDTO data, String channelName) {
        data.setMqTime(System.currentTimeMillis());
        llmAudioPublisherService.publishToDevice(channelName, data);
    }

    /**
     * 发生异常时的处理逻辑。
     * <p>
     * 记录日志，并通过消息队列通知前端发生错误。
     *
     * @param userId      用户ID
     * @param device      设备标识
     * @param channelName 音频通道名称
     * @param error       异常对象
     */
    @Override
    protected void onError(Integer userId, String device, String channelName, Throwable error) {
        log.error("Llm 流式返回异常: userId={}, device={}, error={}", userId, device, error.getMessage(), error);
        // 向客户端推送LLM音频错误消息
        imMqProducer.sendMsg(
                ImMqTopicEnum.REDIS_CHANNEL_PUSH,
                RedisChannelPushTopTagEnum.PUSH_LLM_AUDIO_ERROR.getTag(),
                device,
                JSONUtil.toJsonStr(new StringDTO(channelName))
        );
    }

    /**
     * 注册当前用户的流订阅关系。
     * <p>
     * 将 Disposable 对象保存到服务中以便统一管理和释放资源。
     *
     * @param userId       用户ID
     * @param disposable   可取消的订阅对象
     */
    @Override
    protected void onRegister(Integer userId, Disposable disposable) {
        // 注册流订阅关系，便于后续管理
        llmSseService.registerStream(userId, TeacherUtils.SSE_TYPE_CHAT, disposable);
    }
}
