package cn.iocoder.byzs.module.ai.service.tts;

import cn.iocoder.byzs.module.ai.dal.dataobject.tts.AiTtsDO;
import cn.iocoder.byzs.module.ai.framework.ai.config.ByzsAiProperties;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

@Service
public class DouBaoTtsService {

    private static final Logger logger = LoggerFactory.getLogger(DouBaoTtsService.class);

    @Resource
    private ByzsAiProperties byzsAiProperties;

    /**
     * 语音合成，返回完整音频数据
     */
    public byte[] convertTextToSpeech(AiTtsDO aiTtsDO, String content, String command) throws IOException {
        // 构建请求
        Request request = buildTtsRequest(aiTtsDO, content, command, OutputFormatEnum.MP3.getName());
        
        // 创建带有超时设置的OkHttpClient
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)  // 连接超时
                .readTimeout(60, TimeUnit.SECONDS)     // 读取超时
                .writeTimeout(30, TimeUnit.SECONDS)    // 写入超时
                .build();
        
        // 发送请求并处理响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                handleErrorResponse(response);
            }

            // 读取响应体并逐行解析JSON，处理SSE流式响应
            if (response.body() != null) {
                try (InputStream inputStream = response.body().byteStream();
                     java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(inputStream))) {
                    String line;
                    StringBuilder base64AudioBuilder = new StringBuilder();
                    ObjectMapper objectMapper = new ObjectMapper();
                    boolean hasAudioData = false;

                    while ((line = reader.readLine()) != null) {
                        if (line.trim().isEmpty()) {
                            continue;
                        }

                        try {
                            // 解析单行JSON
                            Map<String, Object> responseMap = objectMapper.readValue(line, Map.class);

                            // 检查响应状态
                            checkResponseStatus(responseMap);

                            // 提取音频数据
                            Object data = responseMap.get("data");
                            if (data instanceof String) {
                                String chunk = data.toString();
                                if (!chunk.isEmpty()) {
                                    base64AudioBuilder.append(chunk);
                                    hasAudioData = true;
                                }
                            }
                        } catch (Exception e) {
                            logger.warn("解析响应行失败: {}", e.getMessage());
                        }
                    }

                    if (hasAudioData && !base64AudioBuilder.isEmpty()) {
                        String base64Audio = base64AudioBuilder.toString();
                        // 解码base64音频数据
                        return java.util.Base64.getDecoder().decode(base64Audio);
                    } else {
                        // 没有音频数据
                        logger.warn("豆包TTS响应没有音频数据");
                        throw new IOException("豆包TTS响应没有音频数据");
                    }
                }
            }
        }
        return new byte[0];
    }

    /**
     * 流式语音合成，实时返回音频数据
     */
    public void streamTextToSpeech(AiTtsDO aiTtsDO, String content, String command, Consumer<byte[]> audioDataCallback) throws IOException {
        // 构建请求
        Request request = buildTtsRequest(aiTtsDO, content, command, "pcm");
        
        // 创建带有超时设置的OkHttpClient
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)  // 连接超时
                .readTimeout(60, TimeUnit.SECONDS)     // 读取超时
                .writeTimeout(30, TimeUnit.SECONDS)    // 写入超时
                .build();
        
        // 发送请求并处理响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                handleErrorResponse(response);
            }

            // 读取响应体并逐行解析JSON，处理SSE流式响应
            if (response.body() != null) {
                try (InputStream inputStream = response.body().byteStream();
                     java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(inputStream))) {
                    String line;
                    ObjectMapper objectMapper = new ObjectMapper();

                    while ((line = reader.readLine()) != null) {
                        if (line.trim().isEmpty()) {
                            continue;
                        }

                        try {
                            // 解析单行JSON
                            Map<String, Object> responseMap = objectMapper.readValue(line, Map.class);

                            // 检查响应状态
                            checkResponseStatus(responseMap);

                            // 提取音频数据
                            Object data = responseMap.get("data");
                            if (data instanceof String) {
                                String chunk = data.toString();
                                if (!chunk.isEmpty() && audioDataCallback != null) {
                                    // 实时解码并返回音频数据
                                    byte[] audioData = java.util.Base64.getDecoder().decode(chunk);
                                    logger.info("豆包TTS流式返回音频数据，长度: {} bytes", audioData.length);
                                    audioDataCallback.accept(audioData);
                                }
                            }
                        } catch (Exception e) {
                            logger.warn("解析响应行失败: {}", e.getMessage());
                        }
                    }
                }
            }
        }
    }

    /**
     * 构建TTS请求
     */
    private Request buildTtsRequest(AiTtsDO aiTtsDO, String content, String command, String format) throws IOException {
        // 获取配置
        ByzsAiProperties.DouBaoProperties doubaoProperties = byzsAiProperties.getDoubao();
        if (doubaoProperties == null) {
            throw new IllegalArgumentException("豆包配置未设置");
        }
        
        ByzsAiProperties.DouBaoProperties.TtsProperties doubaoTtsProperties = doubaoProperties.getTts();
        if (doubaoTtsProperties == null) {
            throw new IllegalArgumentException("豆包TTS配置未设置");
        }

        String ttsUrl = doubaoTtsProperties.getBaseUrl() != null ? doubaoTtsProperties.getBaseUrl() : "https://openspeech.bytedance.com/api/v3/tts/unidirectional";
        String appId = doubaoTtsProperties.getAppId();
        String accessKey = doubaoTtsProperties.getAccessToken() != null ? doubaoTtsProperties.getAccessToken() : doubaoTtsProperties.getAccessKey();
        String resourceId = doubaoTtsProperties.getResourceId() != null ? doubaoTtsProperties.getResourceId() : "seed-tts-2.0";

        if (appId == null || accessKey == null) {
            throw new IllegalArgumentException("豆包TTS配置不完整，缺少appId或accessKey");
        }

        // 构建请求体
        Map<String, Object> requestMap = new HashMap<>();
        Map<String, Object> reqParams = new HashMap<>();
        reqParams.put("speaker", aiTtsDO.getModel()); // 使用配置的音色
        reqParams.put("text", content); // 待合成文本
        
        Map<String, Object> audioParams = new HashMap<>();
        audioParams.put("format", format); // 输出音频格式
        audioParams.put("sample_rate", SampleRateEnum.SAMPLE_RATE_16K.value); // 推荐采样率
        if (aiTtsDO.getEmotion() != null && !aiTtsDO.getEmotion().isEmpty()) {
            audioParams.put("emotion", aiTtsDO.getEmotion());
        }

        // 语速和音量参数
        if (aiTtsDO.getSpeechRate() != null) {
            reqParams.put("speech_rate", aiTtsDO.getSpeechRate());
        }
        if (aiTtsDO.getVolume() != null) {
            reqParams.put("loudness_rate", aiTtsDO.getVolume());
        }

        reqParams.put("audio_params", audioParams);

        // 额外参数
        Map<String, Object> additions = new HashMap<>();
        //音调
        Map<String, Object> post_process = new HashMap<>();
        post_process.put("pitch", aiTtsDO.getVolume());
        additions.put("post_process", post_process);

        //语音指令
        if (command != null && !command.isEmpty()) {
            additions.put("context_texts", List.of(command));
        } else if (aiTtsDO.getCommand() != null && !aiTtsDO.getCommand().isEmpty()) {
            additions.put("context_texts", List.of(aiTtsDO.getCommand()));
        }

        // 将 additions 映射序列化为 JSON 字符串
        ObjectMapper objectMapper = new ObjectMapper();
        String additionsJson = objectMapper.writeValueAsString(additions);
        reqParams.put("additions", additionsJson);
        requestMap.put("req_params", reqParams);

        // 转换为JSON字符串
        String requestBody = objectMapper.writeValueAsString(requestMap);

        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestBody);
        String requestId = UUID.randomUUID().toString();

        // 构建请求
        return new Request.Builder()
                .url(ttsUrl)
                .addHeader("X-Api-App-Id", appId)
                .addHeader("X-Api-Access-Key", accessKey)
                .addHeader("X-Api-Resource-Id", resourceId)
                .addHeader("X-Api-Request-Id", requestId)
                .addHeader("Content-Type", "application/json")
                .post(body)
                .build();
    }

    /**
     * 处理错误响应
     */
    private void handleErrorResponse(Response response) throws IOException {
        String errorBody = response.body() != null ? response.body().string() : "无响应体";
        logger.error("豆包TTS请求失败，状态码: {}, 响应: {}, 错误体: {}", 
                response.code(), response.message(), errorBody);
        throw new IOException("请求失败: " + response + "，错误体: " + errorBody);
    }

    /**
     * 检查响应状态
     */
    private void checkResponseStatus(Map<String, Object> responseMap) throws IOException {
        int code = (int) responseMap.get("code");
        if (code != 0 && code != 20000000) {
            String message = (String) responseMap.get("message");
            throw new IOException("豆包TTS服务返回错误: code=" + code + ", message=" + message);
        }
    }
}