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

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seer.teach.admin.convert.AdminTeachTextAudiosConvert;
import com.seer.teach.admin.request.TeachTextAudiosQueryReq;
import com.seer.teach.admin.request.TeachTextAudiosReq;
import com.seer.teach.admin.response.TeachTextAudiosResp;
import com.seer.teach.admin.service.AdminTeachTextAudiosService;
import com.seer.teach.common.enums.AiScenarioCodeEnum;
import com.seer.teach.teacher.module.entity.ModificationLogEntity;
import com.seer.teach.teacher.module.entity.TeachGoalsEntity;
import com.seer.teach.teacher.module.entity.TeachTextAudiosEntity;
import com.seer.teach.teacher.module.entity.TeachTextsEntity;
import com.seer.teach.teacher.module.mapper.TeachTextAudiosMapper;
import com.seer.teach.teacher.service.AiModelCallService;
import com.seer.teach.teacher.service.TeachTextAudiosService;
import com.seer.teach.teacher.service.TeachTextsService;
import com.seer.teach.teacher.service.platform.AudioWord;
import com.seer.teach.teacher.service.platform.LlmResponse;
import com.seer.teach.teacher.service.platform.StreamingTextToAudioResult;
import com.seer.teach.teacher.service.platform.TextToAudioResult;
import com.seer.teach.teacher.util.ModificationLogUtils;
import com.seer.teach.teacher.module.entity.ModificationLogEntity;
import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
import reactor.core.scheduler.Schedulers;

import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * <p>
 * 讲课稿音频和字幕管理服务实现类
 * </p>
 *
 * @author Lingma
 * @since 2025-12-12
 */
@Slf4j
@Service
public class AdminTeachTextAudiosServiceImpl implements AdminTeachTextAudiosService {

    private static final Semaphore semaphore = new Semaphore(100);

    @Autowired
    private TeachTextAudiosService teachTextAudiosService;

    @Autowired
    private AiModelCallService aiModelCallService;

    @Autowired
    private TeachTextsService teachTextsService;

    @Autowired
    private Executor taskExecutor;

    @Override
    public IPage<TeachTextAudiosResp> pageTeachTextAudios(TeachTextAudiosQueryReq req) {
        log.info("分页查询讲课稿音频和字幕列表, 查询参数: {}", req);
        Page<TeachTextAudiosEntity> page = new Page<>(req.getPageNo(), req.getPageSize());

        // 构建查询条件
        LambdaQueryWrapper<TeachTextAudiosEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(req.getTextId() != null, TeachTextAudiosEntity::getTextId, req.getTextId());

        IPage<TeachTextAudiosEntity> pageResult = teachTextAudiosService.page(page, queryWrapper);

        // 转换为响应对象
        IPage<TeachTextAudiosResp> respPage = pageResult.convert(AdminTeachTextAudiosConvert.INSTANCE::toTeachTextAudiosResp);
        if (CollectionUtils.isEmpty(respPage.getRecords())) {
            return new Page<>(req.getPageNo(), req.getPageSize());
        }
        log.info("分页查询讲课稿音频和字幕列表成功, 总数: {}", respPage.getTotal());
        return respPage;
    }

    @Override
    public TeachTextAudiosResp getTeachTextAudioById(Integer id) {
        log.info("根据ID查询讲课稿音频和字幕详情, ID: {}", id);
        TeachTextAudiosEntity entity = teachTextAudiosService.getById(id);
        TeachTextAudiosResp resp = AdminTeachTextAudiosConvert.INSTANCE.toTeachTextAudiosResp(entity);

        // 记录查看日志
        ModificationLogUtils.logModification(
                ModificationLogEntity.TARGET_TYPE_TEACH_TEXT_AUDIOS,
                id,
                "讲课稿音频和字幕",
                ModificationLogEntity.OPERATION_TYPE_VIEW,
                StpUtil.getLoginIdAsInt(),
                "查看讲课稿音频和字幕详情"
        );
        log.info("查询讲课稿音频和字幕详情成功, ID: {}", id);
        return resp;
    }

    @Override
    public List<TeachTextAudiosResp> getTextAudiosByTextId(Integer textId) {
        log.info("根据讲课稿ID查询对应的音频和字幕列表, 讲课稿ID: {}", textId);
        List<TeachTextAudiosEntity> entities = teachTextAudiosService.getTextAudiosByTextId(textId);
        if (CollectionUtils.isEmpty(entities)) {
            return Collections.emptyList();
        }
        List<TeachTextAudiosResp> respList = entities.stream().map(AdminTeachTextAudiosConvert.INSTANCE::toTeachTextAudiosResp).collect(Collectors.toList());
        log.info("查询讲课稿音频和字幕列表成功, 讲课稿ID: {}, 结果数量: {}", textId, entities.size());
        return respList;
    }

    @Override
    public boolean generatedTeachTextAudio(TeachTextAudiosReq req) {
        TeachTextsEntity text = teachTextsService.getById(req.getTextId());
        teachTextAudiosService.remove(new LambdaQueryWrapper<TeachTextAudiosEntity>().eq(TeachTextAudiosEntity::getTextId, req.getTextId()));
        Map<String, Object> params = new HashMap<>();
        Queue<StreamingTextToAudioResult> response = aiModelCallService.asyncCallText2Audio(AiScenarioCodeEnum.TEXT_TO_SPEECH.getCode(), text.getText(), params, StpUtil.getLoginIdAsInt());
        saveAudioDataToDatabase(req.getTextId(), response);
        return true;
    }

    @Override
    public boolean deleteTeachTextAudioByTextId(Integer textId) {
        log.info("删除讲课稿音频和字幕, textId: {}", textId);
        boolean result = teachTextAudiosService.remove(new LambdaQueryWrapper<>(TeachTextAudiosEntity.class).eq(TeachTextAudiosEntity::getTextId, textId));
        log.info("删除讲课稿音频和字幕结果: {}", result);
        return result;
    }

    @Override
    public Flux<ServerSentEvent<StreamingTextToAudioResult>> streamPlayTeachTextAudioSse(Integer textId) {
        log.info("流式播放课稿音频(SSE)，请求参数: {}", textId);

        if (!semaphore.tryAcquire()) {
            log.warn("系统繁忙，无法处理更多请求，textId: {}", textId);
            return Flux.just(createErrorEvent(textId, "系统繁忙，请稍后再试"));
        }

        AtomicBoolean released = new AtomicBoolean(false);
        Runnable safeRelease = () -> {
            if (released.compareAndSet(false, true)) {
                semaphore.release();
            }
        };

        TeachTextsEntity text = teachTextsService.getById(textId);
        if (text == null) {
            safeRelease.run();
            return Flux.just(createErrorEvent(textId, "未找到对应的讲课稿"));
        }

        List<TeachTextAudiosEntity> audios = teachTextAudiosService.getTextAudiosByTextId(textId);
        if (CollectionUtils.isEmpty(audios)) {
            log.info("未找到已有音频数据，将调用AI服务生成音频，textId: {}", textId);
            return createAiAudioFlux(textId, text.getText(), safeRelease);
        }
        // 当存在已有音频数据时，直接流式返回
        log.info("找到{}条已有音频数据，直接流式返回，textId: {}", audios.size(), textId);
        return createReplayAudioFlux(audios, textId, safeRelease);
    }

    private Flux<ServerSentEvent<StreamingTextToAudioResult>> createAiAudioFlux(
            Integer textId, String textContent, Runnable safeRelease) {

        ConcurrentLinkedQueue<StreamingTextToAudioResult> buffer = new ConcurrentLinkedQueue<>();

        Map<String, Object> params = new HashMap<>();

        return aiModelCallService.streamText2Audio(
                        AiScenarioCodeEnum.TEXT_TO_SPEECH.getCode(),
                        textContent,
                        params,
                        StpUtil.getLoginIdAsInt()
                )
                .doOnNext(sse -> buffer.add(sse.data()))
                .doOnComplete(() -> {
                    CompletableFuture.runAsync(() -> {
                        try {
                            saveAudioDataToDatabase(textId, buffer);
                            log.info("AI 音频生成并保存成功, textId: {}", textId);
                        } catch (Exception e) {
                            log.error("保存 AI 音频失败", e);
                        } finally {
                            safeRelease.run();
                        }
                    }, taskExecutor);
                })
                .doOnCancel(() -> {
                    log.info("客户端取消 AI 音频流, textId: {}, 已接收 {} 条", textId, buffer.size());
                    // 可选：保存部分数据
                    safeRelease.run();
                })
                .onErrorResume(throwable -> {
                    log.error("AI 音频流异常, textId: {}", textId, throwable);
                    safeRelease.run();
                    return Flux.just(createErrorEvent(textId, "音频生成失败: " + throwable.getMessage()));
                });
    }

    private Flux<ServerSentEvent<StreamingTextToAudioResult>> createReplayAudioFlux(
            List<TeachTextAudiosEntity> audios, Integer textId, Runnable safeRelease) {
        return Flux.fromIterable(audios)
                .delayElements(Duration.ofMillis(50))
                .map(this::convertToResult)
                .map(result -> ServerSentEvent.<StreamingTextToAudioResult>builder()
                        .id(String.valueOf(result.getAudioId()))
                        .data(result)
                        .build())
                .concatWith(Flux.just(
                        ServerSentEvent.<StreamingTextToAudioResult>builder()
                                .event("complete")
                                .id(String.valueOf(textId))
                                .data(createCompletionResult())
                                .build()
                ))
                .doOnComplete(safeRelease)
                .doOnCancel(safeRelease)
                .onErrorResume(throwable -> {
                    log.error("回放音频流异常", throwable);
                    safeRelease.run();
                    return Flux.just(createErrorEvent(textId, "播放失败"));
                });
    }

    private StreamingTextToAudioResult convertToResult(TeachTextAudiosEntity audio) {
        StreamingTextToAudioResult result = new StreamingTextToAudioResult();
        result.setAudioId(audio.getId());
        result.setAudioText(audio.getData());
        result.setFinal(audio.getIsFinal() == 1);
        result.setSentence(audio.getSentence());
        return result;
    }

    /**
     * 创建错误事件
     *
     * @param textId   文本ID
     * @param errorMsg 错误信息
     * @return ServerSentEvent
     */
    private ServerSentEvent<StreamingTextToAudioResult> createErrorEvent(Integer textId, String errorMsg) {
        StreamingTextToAudioResult errorResult = new StreamingTextToAudioResult();
        errorResult.setFinal(true);
        errorResult.setStreamingCompleted(true);
        // 可以在实际应用中添加更多错误信息字段
        return ServerSentEvent.<StreamingTextToAudioResult>builder()
                .event("error")
                .id(String.valueOf(textId))
                .data(errorResult)
                .build();
    }

    /**
     * 创建完成事件的结果对象
     *
     * @return StreamingTextToAudioResult
     */
    private StreamingTextToAudioResult createCompletionResult() {
        StreamingTextToAudioResult result = new StreamingTextToAudioResult();
        result.setFinal(true);
        result.setStreamingCompleted(true);
        return result;
    }

    /**
     * 保存音频数据到数据库
     *
     * @param textId         讲课稿ID
     * @param audioDataQueue 音频数据队列
     */
    private void saveAudioDataToDatabase(Integer textId, Queue<StreamingTextToAudioResult> audioDataQueue) {
        try {
            log.info("开始保存音频数据到数据库，textId: {}", textId);
            if (CollectionUtil.isEmpty(audioDataQueue)) {
                log.error("调用模型失败，未返回有效结果");
                return;
            }
            // 将队列转换为列表并排序
            List<StreamingTextToAudioResult> audioDataList = new ArrayList<>(audioDataQueue);

            // 创建要保存的实体列表
            List<TeachTextAudiosEntity> entities = new ArrayList<>();
            int sort = 0;

            for (StreamingTextToAudioResult result : audioDataList) {
                TeachTextAudiosEntity entity = new TeachTextAudiosEntity();
                entity.setTextId(textId);
                entity.setSort(sort++);
                entity.setIsFinal(result.isFinal() ? 1 : 0);
                String data = null;
                // 处理音频数据
                if (Objects.nonNull(result.getAudioData())) {
                    byte[] audioBytes = new byte[result.getAudioData().remaining()];
                    result.getAudioData().get(audioBytes);
                    data = Base64.getEncoder().encodeToString(audioBytes);
                } else {
                    data = result.getAudioText();
                }
                if (StringUtils.isBlank(data) && StringUtils.isBlank(result.getSentence())) {
                    log.error("音频数据为空和字幕数据都为空,textId:{}", textId);
                    continue;
                }
                entity.setData(data);
                if (Objects.nonNull(result.getWord())) {
                    entity.setText(result.getWord().getText());
                }
                if(StringUtils.isNotEmpty(result.getSentence())){
                    entity.setSentence(result.getSentence());
                }
                entities.add(entity);
            }
            // 批量保存到数据库
            if (!entities.isEmpty()) {
                teachTextAudiosService.saveBatch(entities);
                log.info("音频数据保存完成，共保存 {} 条记录", entities.size());
            } else {
                log.warn("没有音频数据需要保存");
            }
        } catch (Exception e) {
            log.error("保存音频数据到数据库时发生错误", e);
        }
    }
}