package com.cmvr.test.flow.runtime.operator.llm;

import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.llm.config.APIProperties;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import com.cmvr.test.util.TextSplitUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;

@Slf4j
@Service
@RequiredArgsConstructor
public class LLMTTSOperateService implements LLMOperateService {

    private final APIProperties apiProperties;

    /**
     * instId -> 音频队列
     */
    private final Map<String, BlockingQueue<AudioSegment>> audioQueues = new ConcurrentHashMap<>();

    /**
     * instId -> 播放线程
     */
    private final Map<String, Thread> playerThreads = new ConcurrentHashMap<>();

    @Override
    public boolean supports(ActionEnum action) {
        return action.name().equals("GENERATE_ADVANCED_AUDIO");
    }

    @Override
    public TaskNodeExecuteResult execute(TaskNodeExecuteMessage message) {
        String instId = message.getInstId();
        JSONObject inputParams = message.getInputParams();
        String text = inputParams.getString("text");

        // 1. 切分
        List<String> slices = TextSplitUtil.splitText(text);
        if (slices.isEmpty()) {
            throw new GlobalException("输入文本为空，无法合成语音");
        }

        // 2. 确保队列和线程存在
        BlockingQueue<AudioSegment> queue = audioQueues.computeIfAbsent(instId, k -> new LinkedBlockingQueue<>());
        startPlayerThread(instId, queue);

        // 3. 逐个生成并放入队列
        CountDownLatch latch = new CountDownLatch(1);
        int index = 1;
        for (String slice : slices) {
            long start = System.currentTimeMillis();
            File audioFile = getAudioFile(slice, index);
            long end = System.currentTimeMillis();
            long playMs = getAudioDurationMs(audioFile);

            // 最后一段带 latch
            AudioSegment segment = new AudioSegment(audioFile.getAbsolutePath(), playMs, end - start,
                    (index == slices.size()) ? latch : null);
            queue.offer(segment);
            log.info("[{}] 音频入队: {}", instId, segment.getPath());
            index++;
        }

        // 4. 等待最后一段播完
        try {
            latch.await();
            log.info("[{}] 最后一段音频播放完成", instId);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new GlobalException("等待音频播放时被中断");
        }

        // 5. 节点才算完成
        JSONObject output = new JSONObject();
        output.put("segments", slices.size());
        return TaskNodeExecuteResult.success(output);
    }

    /**
     * 启动播放线程（按 instId 单独维护）
     */
    private void startPlayerThread(String instId, BlockingQueue<AudioSegment> queue) {
        playerThreads.computeIfAbsent(instId, k -> {
            Thread thread = new Thread(() -> {
                try {
                    while (true) {
                        AudioSegment segment = queue.take(); // 阻塞等待
                        playAudio(segment);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.info("[{}] 播放线程被中断，退出", instId);
                }
            }, "AudioPlayer-" + instId);
            thread.start();
            return thread;
        });
    }


    @NotNull
    private File getAudioFile(String text, int index) {
        Map<String, Object> body = new HashMap<>();
        body.put("text", text);
        body.put("dialect", "普通话");
        body.put("timbre", "员工女");
        body.put("language", "中文");
        body.put("tone", "中性");

        HttpResponse response = HttpRequest.post(apiProperties.getGenerateAdvancedAudio())
                .header("Content-Type", "application/json")
                .body(JSON.toJSONString(body))
                .execute();

        if (!response.isOk()) {
            throw new GlobalException("音频生成失败，状态码: " + response.getStatus());
        }

        byte[] audioData = response.bodyBytes();
        String filename = System.currentTimeMillis() + "_" + index + ".wav";
        String saveDir = "C:\\Users\\11158\\Desktop\\iot";
        File dir = new File(saveDir);
        if (!dir.exists() && !dir.mkdirs()) {
            throw new GlobalException("音频保存目录创建失败: " + saveDir);
        }

        return FileUtil.writeBytes(audioData, new File(dir, filename));
    }

    private long getAudioDurationMs(File file) {
        try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file)) {
            AudioFormat format = audioInputStream.getFormat();
            long frames = audioInputStream.getFrameLength();
            double durationInSeconds = (frames + 0.0) / format.getFrameRate();
            return (long) (durationInSeconds * 1000);
        } catch (UnsupportedAudioFileException | IOException e) {
            throw new GlobalException("获取音频时长失败: {}", e.getMessage());
        }
    }
    
    /**
     * 播放一个音频文件（阻塞直到播完）
     */
    private void playAudio(AudioSegment segment) {
        String filePath = segment.getPath();
        try (AudioInputStream sourceStream = AudioSystem.getAudioInputStream(new File(filePath))) {
            AudioFormat baseFormat = sourceStream.getFormat();
            AudioFormat targetFormat = new AudioFormat(
                    AudioFormat.Encoding.PCM_SIGNED,
                    baseFormat.getSampleRate(),
                    16,
                    baseFormat.getChannels(),
                    baseFormat.getChannels() * 2,
                    baseFormat.getSampleRate(),
                    false
            );

            AudioInputStream pcmStream = AudioSystem.getAudioInputStream(targetFormat, sourceStream);

            DataLine.Info info = new DataLine.Info(SourceDataLine.class, targetFormat);
            try (SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info)) {
                line.open(targetFormat);
                line.start();

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = pcmStream.read(buffer, 0, buffer.length)) != -1) {
                    line.write(buffer, 0, bytesRead);
                }

                line.drain();
                line.stop();
            }

            log.info("播放完成: {}", filePath);

            // 如果有 latch，释放
            if (segment.getLatch() != null) {
                segment.getLatch().countDown();
            }

        } catch (Exception e) {
            throw new GlobalException("播放失败: ", e);
        }
    }

    @Data
    public static class AudioSegment {
        private final String path;
        private final long playMs;
        private final long generateMs;
        private final CountDownLatch latch; // 仅最后一段有
    }
}