package com.seer.teach.teacher.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.LessonStateEvent;
import com.seer.teach.common.enums.teacher.TaskStatusEnum;
import com.seer.teach.iot.api.UserDeviceServiceApi;
import com.seer.teach.teacher.common.dto.TeachAudios;
import com.seer.teach.teacher.common.dto.TeachDTO;
import com.seer.teach.teacher.common.req.StudyReq;
import com.seer.teach.teacher.common.utils.TeacherUtils;
import com.seer.teach.teacher.convert.LessonConvert;
import com.seer.teach.teacher.convert.TeachConvert;
import com.seer.teach.teacher.module.entity.LessonRecordsEntity;
import com.seer.teach.teacher.module.entity.LessonsEntity;
import com.seer.teach.teacher.module.mapper.LessonsMapper;
import com.seer.teach.teacher.mq.ImMqProducer;
import com.seer.teach.teacher.service.LessonRecordsService;
import com.seer.teach.teacher.service.LessonsService;
import com.seer.teach.teacher.service.TeachService;
import com.seer.teach.user.api.UserInfoServiceApi;
import com.seer.teach.user.api.dto.UserInfoDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 课程服务实现类，提供课程启动、继续下一课等功能。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LessonsServiceImpl extends ServiceImpl<LessonsMapper, LessonsEntity> implements LessonsService {

    private final LlmAudioPublisherService llmAudioPublisherService;

    private final LessonStateMachineService stateMachineService;

    private final LessonRecordsService lessonRecordsService;

    private final UserDeviceServiceApi userDeviceServiceApi;

    private final UserInfoServiceApi userInfoServiceApi;

    private final TeachService teachService;

    private final ImMqProducer imMqProducer;

    /**
     * 启动一次新的课程学习流程。
     *
     * @param params 学习请求参数，包含知识点ID、学科ID等信息
     */
    @Override
    public void startLesson(StudyReq params) {
        // 获取当前登录用户的ID
        Integer userId = StpUtil.getLoginIdAsInt();
        // 根据用户ID获取其绑定的设备标识
        String device = userDeviceServiceApi.getDeviceByUserId(userId);
        // 获取首次教学内容的数据
        TeachDTO teachData = teachService.firstLesson(params.getId());
        teachData.setDevice(device);
        // 保存本次课程的信息并获得任务ID
        Integer taskId = saveLesson(userId, params.getId(), params.getSubjectId());
        // 记录该课程的学习记录
        LessonRecordsEntity record = lessonRecordsService.saveLessonRecord(taskId, teachData.getGoalId(), teachData.getTextId());
        teachData.setRecordId(record.getId());
        // 将用户状态设置为正在学习，并缓存教学数据
        stateMachineService.sendEvent(userId, LessonStateEvent.START_LESSON);
        stateMachineService.putTeach(userId, teachData);
        // 生成会话ID
        Integer sessionId = TeacherUtils.generateSessionId(userId, teachData);
        // 获取并发送频道名给客户端
        String channelName = getAndSendChannelName(device, sessionId);
        log.info("用户[{}],开始学习 知识点：[{}] 教学目标:[{}] 教学重点:[{}] ChannelName:[{}]", userId, teachData.getKnowledgePointName(), teachData.getGoal(), teachData.getPoint(), channelName);
        // 发送知识点相关数据到IM消息队列
        imMqProducer.sendLessonKnowData(teachData, device);
        // 推送音频数据至指定频道
        pushAudioData(userId, teachData.getAudios(), sessionId, channelName);
    }

    /**
     * 开始下一个课程环节（如进入下一条教学目标）。
     *
     * @param teachDTO 当前教学数据传输对象，包括上一阶段的教学信息及设备信息
     */
    @Override
    public void startNextLesson(TeachDTO teachDTO,Integer userId) {
        Integer knowId = teachDTO.getId();
        String device = teachDTO.getDevice();
        Integer sort = teachDTO.getSort();
        lessonRecordsService.updateEndTime(teachDTO.getRecordId());
        Boolean isLastOneGoal = teachService.checkIsLastOneGoal(knowId, sort);
        if (isLastOneGoal) {
            updateLessonStudyEndTime(userId);
            imMqProducer.sendClassOver(device);
        }
        teachService.nextLesson(teachDTO);
        // 生成会话ID
        Integer sessionId = TeacherUtils.generateSessionId(userId, teachDTO);
        // 获取并发送频道名给客户端
        String channelName = getAndSendChannelName(teachDTO.getDevice(), sessionId);
        log.info("用户[{}],开始新教学目标:[{}] 教学目标ID:[{}] ChannelName:[{}]", userId, teachDTO.getGoal(), teachDTO.getGoalId(), channelName);
        // 发送知识点相关数据到IM消息队列
        imMqProducer.sendLessonKnowData(teachDTO, device);
        // 推送音频数据至指定频道
        pushAudioData(userId, teachDTO.getAudios(), sessionId, channelName);
    }

    /**
     * 更新当前课程的学习结束时间及相关统计字段。
     *
     * @param userId 用户ID
     */
    private void updateLessonStudyEndTime(Integer userId) {
        LessonsEntity task = this.lambdaQuery()
                .eq(LessonsEntity::getUserId, userId)
                .orderByDesc(LessonsEntity::getCreateTime)
                .last("LIMIT 1")
                .one();
        LocalDateTime now = LocalDateTime.now();
        int seconds = (int) Duration.between(task.getCreateTime(), now).getSeconds();
        this.lambdaUpdate()
                .eq(LessonsEntity::getId, task.getId())
                .set(LessonsEntity::getStudyEndTime, now)
                .set(LessonsEntity::getStudySeconds, seconds)
                .set(LessonsEntity::getStatus, TaskStatusEnum.FINISH_STUDY.getCode())
                .update();
    }

    /**
     * 将音频数据推送到指定频道中供播放使用。
     *
     * @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);
        });
    }

    /**
     * 构造频道名称并通过消息队列通知客户端使用的频道名。
     *
     * @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;
    }

    /**
     * 保存课程信息并返回课程ID。
     *
     * @param userId    用户ID
     * @param knowId    知识点ID
     * @param subjectId 学科ID
     * @return 新创建的课程ID
     */
    public Integer saveLesson(Integer userId, Integer knowId, Integer subjectId) {
        // 获取用户详细信息以取得年级ID
        UserInfoDTO childrenInfo = userInfoServiceApi.getById(userId);
        Integer gradeId = childrenInfo.getGradeId();
        // 创建课程实体对象
        LessonsEntity lessonsEntity = LessonConvert.INSTANCE.startLesson(userId, knowId, subjectId, gradeId, TaskStatusEnum.STUDYING.getCode(), LocalDateTime.now());
        // 持久化课程信息
        this.save(lessonsEntity);
        // 返回课程ID
        return lessonsEntity.getId();
    }

}
