import axios from 'axios';
import fs from 'fs';
import path from 'path';
import { config } from '../../config';
import { VoiceParams } from '../../types';
import { ITtsProvider } from './provider.interface';
import { withAiLog } from '../../services/ai-call-logger';

/**
 * 阿里云百炼 Qwen-TTS Provider（同步模式）
 *
 * 接口限制参考:
 *   - Qwen-TTS (multimodal-generation): 单次 20,000 字符（同步/流式）
 *   - CosyVoice (SpeechSynthesizer): 单次 20,000 字符（同步/流式）
 *   - 传统长文本合成: 80,000 字符（建议 40,000 以内）
 *   - 传统基础合成: 300 字符（已弃用）
 *
 * 调用方式:
 *   - 同步非流式: POST → 直接返回音频 URL
 *   - SSE 流式: 加 X-DashScope-SSE: enable header
 *   - ⚠️ 不支持 X-DashScope-Async 异步模式
 *
 * 当前策略: 使用同步模式 + 1000 字符分段，每段快速返回音频 URL
 *
 * 文档: https://help.aliyun.com/zh/model-studio/qwen-tts
 */
export class AliyunTtsProvider implements ITtsProvider {
  readonly name: string;
  readonly vendor = 'bailian';
  readonly mode = 'sync' as const;
  maxTextLength = 0; // 由 models.json 配置，未配置时用默认 1000
  readonly concurrency = 2;
  readonly modelId: string;
  readonly ttsApiPath: string;

  private apiKey: string;
  private voice: string;

  /**
   * @param vendorKey  供应商 key（如 'bailian'）
   * @param apiKey     API Key
   * @param modelId    TTS 模型 ID（来自 models.json，如 'qwen3-tts-instruct-flash'）
   * @param ttsApiPath TTS API 完整路径（来自 models.json vendor 的 ttsApiPath）
   */
  constructor(vendorKey: string = 'bailian', apiKey?: string, modelId?: string, ttsApiPath?: string, maxTextLength?: number) {
    this.name = `${vendorKey}-tts`;
    // 从 models.json 获取默认 TTS 模型（兜底：qwen3-tts-instruct-flash）
    this.modelId = modelId || (config.models as any).tts?.defaultModel || 'qwen3-tts-instruct-flash';
    this.ttsApiPath = ttsApiPath || 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation';
    if (maxTextLength) this.maxTextLength = maxTextLength;
    if (apiKey) {
      this.apiKey = apiKey;
    } else {
      // 从 models.json 获取对应 vendor 的 apiKey
      const vendorConfig = (config.models as any).vendors?.[vendorKey];
      this.apiKey = vendorConfig?.apiKey || '';
    }
    // 默认音色从 models.json TTS 配置读取
    this.voice = (config.models as any).tts?.defaultVoice || 'Cherry';
  }

  /**
   * 语音合成（CosyVoice SSE 流式，千问同步非流式）
   */
  async synthesize(
    text: string,
    voiceId: string,
    params: VoiceParams,
    outputPath: string,
    retries: number = 3,
    modelOverride?: string,
  ): Promise<string> {
    const activeModel = modelOverride || this.modelId;
    const isCosyVoice = activeModel.includes('cosyvoice');
    let lastError: Error | null = null;

    for (let attempt = 1; attempt <= retries; attempt++) {
      try {
        // 构建请求体
        const requestBody: any = {
          model: activeModel,
          input: {
            text: text,
            voice: voiceId || this.voice,
            ...(isCosyVoice ? {} : { language_type: 'Chinese' }),
          },
        };
        if (isCosyVoice) {
          requestBody.input.format = 'mp3';
          requestBody.input.sample_rate = 24000;
          const parts: string[] = [];
          // Instrut 情感/场景控制（优先级最高，放在最前面）
          if ((params as any).instructText) parts.push((params as any).instructText);
          if (params.speed !== undefined && params.speed !== 1) parts.push(`语速${params.speed > 1 ? '偏快' : '偏慢'}`);
          if (params.pitch !== undefined && params.pitch !== 0) parts.push(`音调${params.pitch > 0 ? '偏高' : '偏低'}`);
          if (params.volume !== undefined && params.volume !== 50) parts.push(`音量${params.volume > 50 ? '较大' : '较小'}`);
          if (parts.length > 0) requestBody.input.instructions = parts.join('，') + '。';
        }
        if (!isCosyVoice && activeModel.includes('instruct')) {
          const instructions: string[] = [];
          if ((params as any).instructText) instructions.push((params as any).instructText);
          if (params.speed !== 1) instructions.push(`语速${params.speed > 1 ? '较快' : '较慢'}`);
          if (params.pitch !== 0) instructions.push(`音调${params.pitch > 0 ? '较高' : '较低'}`);
          if (instructions.length > 0) requestBody.input.instructions = instructions.join('，');
        }

        console.log(`📤 [Aliyun TTS] ${isCosyVoice?'CosyVoice SSE':'Qwen'} ${attempt}/${retries}, model: ${activeModel}, voice: ${voiceId || this.voice}, text: ${text.length}字`);

        // CosyVoice: SSE 流式，边收边存，无超时
        if (isCosyVoice) {
          const result = await withAiLog(
            () => this.synthesizeStream(requestBody, outputPath),
            { callType: 'tts_synthesize', provider: this.vendor, model: activeModel, textLen: text.length }
          );
          return result;
        }

        // 千问: 同步非流式
        const response = await withAiLog(
          () => axios.post(this.ttsApiPath, requestBody, {
            headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' },
            timeout: 600000,
          }),
          { callType: 'tts_synthesize', provider: this.vendor, model: activeModel, textLen: text.length }
        );

        if (response.status !== 200) throw new Error(`Aliyun TTS 请求失败: HTTP ${response.status}`);
        const data = response.data;
        if (data.code) throw new Error(`Aliyun TTS 错误: ${data.message || JSON.stringify(data)}`);
        const audioUrl = data.output?.audio?.url;
        if (!audioUrl) throw new Error(`Aliyun TTS 未返回音频 URL`);

        console.log(`🔗 [Aliyun TTS] 获取音频 URL: ${audioUrl.substring(0, 80)}...`);

        // 下载音频文件
        try {
          return await this.downloadAudio(audioUrl, outputPath);
        } catch (downloadError: any) {
          console.warn(`⚠️ [Aliyun TTS] 音频下载失败: ${downloadError.message}`);
          // 下载失败，返回 cloud: URL 标记（后续会处理）
          return `cloud:${audioUrl}`;
        }
      } catch (error: any) {
        const errorDetails = error.response?.data || error.message || '';
        const errorStr = typeof errorDetails === 'string' ? errorDetails : JSON.stringify(errorDetails);
        const isRateLimit = error.response?.status === 429 || errorStr.includes('Throttling.RateQuota');
        const isServerError = error.response?.status >= 500
          || errorStr.includes('InternalError')      // 阿里内部错误，可重试
          || errorStr.includes('timeout')            // 流式超时，可重试
          || errorStr.includes('CircuitBreaker');    // 熔断器开启，切换重试

        console.error(`❌ [Aliyun TTS] 失败 (尝试 ${attempt}/${retries}):`, error.message);

        if ((isRateLimit || isServerError) && attempt < retries) {
          const waitTime = Math.pow(2, attempt) * 1000;
          console.warn(`⏳ 等待 ${waitTime}ms 后重试...`);
          await new Promise(resolve => setTimeout(resolve, waitTime));
          lastError = new Error(`Aliyun TTS 临时错误: ${error.message}`);
          continue;
        }

        lastError = new Error(`Aliyun TTS 调用失败: ${error.message}`);
      }
    }

    throw lastError || new Error('Aliyun TTS 服务调用失败');
  }

  /**
   * CosyVoice SSE 流式合成：边生成边接收，无超时限制
   */
  private async synthesizeStream(requestBody: any, outputPath: string): Promise<string> {
    console.log(`🔊 [Aliyun SSE] 请求: ${this.ttsApiPath}, model=${this.modelId}, voice=${requestBody.input?.voice || 'default'}, textLen=${requestBody.input?.text?.length || 0}`);
    const response = await axios.post(this.ttsApiPath, requestBody, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
        'X-DashScope-SSE': 'enable',
      },
      responseType: 'stream',
      timeout: 0,
    });
    console.log(`📡 [Aliyun SSE] 响应: HTTP ${response.status}, contentType=${response.headers['content-type']}`);

    if (response.status !== 200) {
      const body = await new Promise<string>((r) => {
        let d = '';
        response.data.on('data', (c: Buffer) => d += c.toString());
        response.data.on('end', () => r(d));
      });
      console.error(`❌ [Aliyun SSE] HTTP ${response.status}: ${body.substring(0, 500)}`);
      throw new Error(`Aliyun SSE HTTP ${response.status}: ${body.substring(0, 200)}`);
    }

    return new Promise((resolve, reject) => {
      const chunks: Buffer[] = [];
      let buffer = '';
      let sseEventCount = 0;
      let audioEventCount = 0;
      let lastErrorMsg = '';
      const lastRawEvents: string[] = []; // 保留最后5个事件用于调试

      response.data.on('data', (chunk: Buffer) => {
        buffer += chunk.toString();
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';

        for (const line of lines) {
          if (line.startsWith('data:')) {
            sseEventCount++;
            const rawData = line.substring(5).trim();

            // 调试：记录最后5个事件
            lastRawEvents.push(rawData.substring(0, 300));
            if (lastRawEvents.length > 5) lastRawEvents.shift();

            try {
              const json = JSON.parse(rawData);

              // 检查是否为错误响应
              if (json.code || json.message) {
                lastErrorMsg = `[${json.code || 'unknown'}] ${json.message || ''}`;
                console.warn(`⚠️ [Aliyun SSE] 错误事件: ${lastErrorMsg}, raw=${rawData.substring(0, 200)}`);
                continue;
              }

              // 提取音频数据
              const audioData = json.output?.audio?.data;
              if (audioData) {
                audioEventCount++;
                chunks.push(Buffer.from(audioData, 'base64'));
              } else {
                // 非音频事件（如 task_id、运行状态等），记录便于调试
                const snippet = JSON.stringify(json).substring(0, 150);
                console.log(`📡 [Aliyun SSE] 非音频事件 #${sseEventCount}: ${snippet}`);
              }
            } catch {
              console.warn(`⚠️ [Aliyun SSE] JSON解析失败: raw=${rawData.substring(0, 200)}`);
            }
          }
        }
      });

      response.data.on('end', () => {
        console.log(`[Aliyun SSE] 流结束: totalEvents=${sseEventCount}, audioEvents=${audioEventCount}, chunks=${chunks.length}`);
        if (chunks.length === 0) {
          const diag = [
            `eventCount=${sseEventCount}`,
            `audioCount=${audioEventCount}`,
            `lastError=${lastErrorMsg || '无错误事件'}`,
            `lastEvents=${JSON.stringify(lastRawEvents)}`,
          ].join(', ');
          reject(new Error(`CosyVoice SSE 未收到音频数据（诊断: ${diag}）`));
          return;
        }
        const audioBuffer = Buffer.concat(chunks);
        const dir = path.dirname(outputPath);
        if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
        fs.writeFileSync(outputPath, audioBuffer);
        console.log(`✅ [Aliyun SSE] 流式完成: ${outputPath} (${audioBuffer.length} bytes)`);
        resolve(outputPath);
      });

      response.data.on('error', (err: any) => {
        console.error(`❌ [Aliyun SSE] Stream error: ${err.message}, chunks=${chunks.length}, events=${sseEventCount}`);
        reject(err);
      });
    });
  }

  /**
   * 下载音频文件到本地
   */
  private async downloadAudio(audioUrl: string, outputPath: string): Promise<string> {
    console.log(`⬇️ [Aliyun TTS] 下载音频: ${audioUrl.substring(0, 80)}...`);

    const response = await withAiLog(
      () => axios.get(audioUrl, {
        responseType: 'arraybuffer',
        timeout: 600000,
      }),
      { callType: 'tts_download', provider: this.vendor, model: this.modelId }
    );

    const dir = path.dirname(outputPath);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }

    // 确保输出路径以 .wav 结尾（Qwen-TTS 返回 wav 格式）
    const finalPath = outputPath.endsWith('.wav') ? outputPath : outputPath.replace(/\.[^.]+$/, '.wav');
    fs.writeFileSync(finalPath, response.data);
    const stats = fs.statSync(finalPath);
    console.log(`✅ [Aliyun TTS] 下载完成: ${finalPath} (${stats.size} bytes)`);

    return finalPath;
  }
}
