package com.fuyuanshen.equipment.utils;

import com.alibaba.nls.client.AccessToken;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

import static cn.dev33.satoken.SaManager.log;

/**
 * 阿里巴巴语音合成工具类
 * 提供文本转语音功能，支持多种声音、语速、音量等参数调节
 */
@Component
public class AlibabaTTSUtil {
    // ========== 常量配置 ==========
    /**
     * 阿里云TTS服务基础URL
     */
    private static final String BASE_URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
    /**
     * 音频内容类型标识
     */
    private static final String CONTENT_TYPE_AUDIO = "audio/mpeg";

    // ========== 默认参数值 ==========
    /**
     * 默认发音人 - 小云
     */
    private static final String DEFAULT_VOICE = "xiaoyun";
    /**
     * 默认音量 50%
     */
    private static final int DEFAULT_VOLUME = 50;
    /**
     * 默认语速 0（正常）
     */
    private static final int DEFAULT_SPEECH_RATE = 1;
    /**
     * 默认语调 0（正常）
     */
    private static final int DEFAULT_PITCH_RATE = 0;
    /**
     * 默认音频格式 pcm
     */
    private static final String DEFAULT_FORMAT = "pcm";
    /**
     * 默认采样率 16000Hz
     */
    private static final int DEFAULT_SAMPLE_RATE = 16000;

    // ========== Token管理配置 ==========
    /**
     * Token刷新缓冲时间（提前5分钟刷新，单位：毫秒）
     */
    private static final long TOKEN_REFRESH_BUFFER = 5 * 60 * 1000L;

    // ========== 配置参数（从配置文件读取） ==========
    @Value("${alibaba.tts.appKey:}")
    private String appkey;
    @Value("${alibaba.tts.akId:}")
    private String aliyunAkId;
    @Value("${alibaba.tts.akSecret:}")
    private String aliyunAkSecret;
    private final String redisKey = "alibaba:TTS:AccessToken";

    // ========== HTTP客户端（单例复用） ==========
    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)    // 连接超时：30秒
            .readTimeout(60, TimeUnit.SECONDS)       // 读取超时：60秒（音频生成较慢）
            .build();

    /**
     * 生成语音文件 - 简化版（使用默认参数）
     *
     * @param text          要转换的文本内容
     * @param audioSaveFile 音频文件保存路径
     * @return true-成功 false-失败
     */
    public boolean generateSpeech(String text, String audioSaveFile) {
        return generateSpeech(text, audioSaveFile, DEFAULT_FORMAT, DEFAULT_SAMPLE_RATE, DEFAULT_VOICE);
    }

    /**
     * 生成语音文件 - 标准版
     *
     * @param text          要转换的文本内容
     * @param audioSaveFile 音频文件保存路径
     * @param format        音频格式（如：mp3, wav等）
     * @param sampleRate    采样率（如：16000, 22050, 44100等）
     * @param voice         发音人（如：xiaoyun, xiaoqian等）
     * @return true-成功 false-失败
     */
    public boolean generateSpeech(String text, String audioSaveFile, String format,
                                  int sampleRate, String voice) {
        return generateSpeech(text, audioSaveFile, format, sampleRate, voice,
                DEFAULT_VOLUME, DEFAULT_SPEECH_RATE, DEFAULT_PITCH_RATE);
    }

    /**
     * 生成语音文件 - 完整版（支持所有参数调节）
     *
     * @param text          要转换的文本内容
     * @param audioSaveFile 音频文件保存路径
     * @param format        音频格式
     * @param sampleRate    采样率
     * @param voice         发音人
     * @param volume        音量（0-100）
     * @param speechRate    语速（-500~500）
     * @param pitchRate     语调（-500~500）
     * @return true-成功 false-失败
     */
    public boolean generateSpeech(String text, String audioSaveFile, String format,
                                  int sampleRate, String voice, int volume,
                                  int speechRate, int pitchRate) {
        try {
            // 参数校验
            validateParameters(text, audioSaveFile, format, sampleRate);

            // 获取访问令牌
            String token = getValidAccessToken();
            if (token == null) {
                log.error("获取访问令牌失败");
                return false;
            }

            // 构建请求URL并执行请求
            String url = buildRequestUrl(text, format, sampleRate, voice, volume, speechRate, pitchRate, token);
            log.debug("TTS请求URL: {}", url.replace(token, "***")); // 隐藏token日志

            return executeRequest(url, audioSaveFile);

        } catch (IllegalArgumentException e) {
            log.error("参数验证失败: {}", e.getMessage());
            return false;
        } catch (Exception e) {
            log.error("语音合成请求失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 生成标准PCM数据（单声道，16K采样率，16bit深度，包含44字节WAV头）
     * 数据总大小不超过2MB，内存操作，无临时文件
     */
    public byte[] generateStandardPcmData(String text) throws IOException {
        String token = getValidAccessToken();
        if (token == null) {
            throw new IOException("获取访问令牌失败");
        }

        String url = buildRequestUrl(text, "pcm", 16000, DEFAULT_VOICE,
                DEFAULT_VOLUME, DEFAULT_SPEECH_RATE, DEFAULT_PITCH_RATE, token);

        // 直接从响应中读取 raw PCM 数据（不写文件）
        byte[] rawPcmData = executeRequestAndGetBytes(url);
        if (rawPcmData == null || rawPcmData.length == 0) {
            throw new IOException("TTS返回音频数据为空");
        }

        return rawPcmData;
    }

    /**
     * 获取有效的访问令牌（优先从缓存获取，缓存不存在则重新生成）
     *
     * @return 访问令牌，获取失败返回null
     */
    private String getValidAccessToken() {
        try {
            // 1. 尝试从Redis缓存获取
            String cachedToken = RedisUtils.getCacheObject(redisKey);
            if (cachedToken != null && !cachedToken.trim().isEmpty()) {
                log.debug("从缓存获取访问令牌成功");
                return cachedToken;
            }

            // 2. 缓存不存在，重新生成
            log.info("缓存中未找到访问令牌，重新生成...");
            return refreshAccessToken();

        } catch (Exception e) {
            log.error("从Redis获取访问令牌失败: {}", e.getMessage());
            return refreshAccessToken(); // 降级处理：直接刷新
        }
    }

    /**
     * 刷新访问令牌（调用阿里云API获取新令牌并缓存）
     *
     * @return 新的访问令牌，获取失败返回null
     */
    private String refreshAccessToken() {
        try {
            // 调用阿里云API获取访问令牌
            AccessToken accessToken = new AccessToken(aliyunAkId, aliyunAkSecret);
            accessToken.apply();
            String token = accessToken.getToken();
            // 缓存令牌到Redis
            RedisUtils.setCacheObject(redisKey, token, Duration.ofMillis(60));
            log.info("访问令牌刷新成功");

            return token;
        } catch (Exception e) {
            log.error("刷新访问令牌失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 参数验证
     *
     * @throws IllegalArgumentException 参数不合法时抛出异常
     */
    private void validateParameters(String text, String audioSaveFile, String format, int sampleRate) {
        if (text == null || text.trim().isEmpty()) {
            throw new IllegalArgumentException("文本内容不能为空");
        }

        // 文本长度限制（阿里云TTS限制）
        if (text.length() > 500) {
            throw new IllegalArgumentException("文本长度超过限制（最大500字符）");
        }

        if (audioSaveFile == null || audioSaveFile.trim().isEmpty()) {
            throw new IllegalArgumentException("音频文件保存路径不能为空");
        }

        if (format == null || format.trim().isEmpty()) {
            throw new IllegalArgumentException("音频格式不能为空");
        }

        // 采样率范围验证
        if (sampleRate <= 0 || sampleRate > 48000) {
            throw new IllegalArgumentException("采样率必须在1-48000范围内");
        }
    }

    /**
     * 构建TTS请求URL
     */
    private String buildRequestUrl(String text, String format, int sampleRate,
                                   String voice, int volume, int speechRate,
                                   int pitchRate, String token) {
        try {
            // URL编码文本内容（防止特殊字符问题）
            String encodedText = URLEncoder.encode(text, StandardCharsets.UTF_8.name());

            // 使用默认发音人如果未指定
            String actualVoice = (voice != null && !voice.trim().isEmpty()) ? voice : DEFAULT_VOICE;

            // 构建请求URL（使用字符串拼接，性能更优）
            return BASE_URL +
                    "?appkey=" + appkey +
                    "&token=" + token +
                    "&text=" + encodedText +
                    "&format=" + format +
                    "&sample_rate=" + sampleRate;
            //"&voice=" + actualVoice +
            //"&volume=" + Math.max(0, Math.min(100, volume)) +        // 音量范围限制
            //"&speech_rate=" + Math.max(-500, Math.min(500, speechRate)) + // 语速范围限制
            //"&pitch_rate=" + Math.max(-500, Math.min(500, pitchRate));   // 语调范围限制

        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8编码不支持", e);
        }
    }

    /**
     * 执行HTTP请求
     */
    private boolean executeRequest(String url, String audioSaveFile) {
        Request request = new Request.Builder()
                .url(url)
                .header("User-Agent", "AlibabaTTSClient/1.0.0")
                .get()
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            // 检查HTTP响应状态
            if (!response.isSuccessful()) {
                log.error("HTTP请求失败，状态码: {}", response.code());
                handleErrorResponse(response);
                return false;
            }

            // 处理成功响应
            return handleResponse(response, audioSaveFile);

        } catch (IOException e) {
            log.error("HTTP请求执行失败: {}", e.getMessage());
            return false;
        }
    }

    private byte[] executeRequestAndGetBytes(String url) {
        Request request = new Request.Builder()
                .url(url)
                .header("User-Agent", "AlibabaTTSClient/1.0.0")
                .get()
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                handleErrorResponse(response);
                return null;
            }

            String contentType = response.header("Content-Type");
            if (!CONTENT_TYPE_AUDIO.equals(contentType) && !contentType.contains("pcm")) {
                handleErrorResponse(response);
                return null;
            }

            byte[] audioData = response.body().bytes();
            log.info("TTS原始PCM获取成功，大小: {} 字节", audioData.length);
            return audioData;

        } catch (IOException e) {
            log.error("HTTP请求执行失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 处理HTTP响应
     */
    private boolean handleResponse(Response response, String audioSaveFile) throws IOException {
        String contentType = response.header("Content-Type");

        // 根据内容类型处理响应
        if (CONTENT_TYPE_AUDIO.equals(contentType)) {
            return saveAudioFile(response, audioSaveFile);  // 保存音频文件
        } else {
            handleErrorResponse(response);  // 处理错误响应
            return false;
        }
    }

    /**
     * 保存音频文件到本地
     */
    private boolean saveAudioFile(Response response, String audioSaveFile) {
        File file = new File(audioSaveFile);
        File parentDir = file.getParentFile();

        // 确保保存目录存在
        if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {
            log.error("创建目录失败: {}", parentDir.getAbsolutePath());
            return false;
        }

        try (FileOutputStream fos = new FileOutputStream(file)) {
            // 读取音频数据并写入文件
            byte[] audioData = response.body().bytes();
            fos.write(audioData);

            log.info("音频文件保存成功: {} ({} 字节)", audioSaveFile, audioData.length);
            return true;

        } catch (IOException e) {
            log.error("保存音频文件失败: {}", e.getMessage());
            // 清理可能已创建的不完整文件
            deleteFileSilently(file);
            return false;
        }
    }

    /**
     * 处理错误响应（非音频内容）
     */
    private void handleErrorResponse(Response response) throws IOException {
        String errorMessage = "未知错误";
        if (response.body() != null) {
            errorMessage = response.body().string();
        }
        log.error("TTS请求失败。状态码: {}, 错误信息: {}", response.code(), errorMessage);
    }

    /**
     * 静默删除文件（不抛出异常）
     */
    private void deleteFileSilently(File file) {
        try {
            if (file.exists() && !file.delete()) {
                log.warn("删除文件失败: {}", file.getAbsolutePath());
            }
        } catch (Exception e) {
            log.warn("删除文件时发生异常: {}", e.getMessage());
        }
    }
}