"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VOICES = void 0;
exports.getAliyunVoice = getAliyunVoice;
exports.shouldUseLongText = shouldUseLongText;
exports.splitText = splitText;
exports.generateAudio = generateAudio;
exports.getAudioStatus = getAudioStatus;
exports.getVoices = getVoices;
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const uuid_1 = require("uuid");
const config_1 = require("../../config");
const aliyun_provider_1 = require("./aliyun.provider");
const aliyun_realtime_provider_1 = require("./aliyun-realtime.provider");
const mock_provider_1 = require("./mock.provider");
const audio_merger_1 = require("./audio-merger");
const ai_summary_service_1 = require("./ai-summary.service");
// 日志文件路径
const LOG_FILE = path_1.default.join(process.cwd(), 'tts-debug.log');
function logToFile(msg) {
    const timestamp = new Date().toISOString();
    fs_1.default.appendFileSync(LOG_FILE, `[${timestamp}] ${msg}\n`);
}
// 可用音色列表（使用阿里云官方音色）
exports.VOICES = [
    { id: 'cherry', name: '芊悦', gender: 'female', description: '阳光积极、亲切自然' },
    { id: 'serena', name: '苏瑶', gender: 'female', description: '温柔女声' },
    { id: 'ethan', name: '晨煦', gender: 'male', description: '阳光温暖、活力男声' },
    { id: 'chelsie', name: '千雪', gender: 'female', description: '二次元虚拟女友' },
    { id: 'momo', name: '茉兔', gender: 'female', description: '撒娇搞怪' },
    { id: 'vivian', name: '十三', gender: 'female', description: '可爱小暴躁' },
    { id: 'moon', name: '月白', gender: 'male', description: '率性帅气' },
    { id: 'maia', name: '四月', gender: 'female', description: '知性温柔' },
    { id: 'kai', name: '凯', gender: 'male', description: '舒缓放松' },
    { id: 'nofish', name: '不吃鱼', gender: 'male', description: '不会翘舌音' },
];
// 前端音色 ID 到阿里云音色的映射
const VOICE_MAPPING = {
    cherry: 'Cherry',
    serena: 'Serena',
    ethan: 'Ethan',
    chelsie: 'Chelsie',
    momo: 'Momo',
    vivian: 'Vivian',
    moon: 'Moon',
    maia: 'Maia',
    kai: 'Kai',
    nofish: 'Nofish',
};
// 获取阿里云音色名称
function getAliyunVoice(voiceId) {
    return VOICE_MAPPING[voiceId] || 'Cherry';
}
// 判断是否使用长文本模式（>5000字符且启用realtime）
// 注意：WebSocket realtime 模式需要特殊的API权限和配置，如果连接失败会导致生成失败。
// 暂时强制禁用，使用 HTTP 分段模式
function shouldUseLongText(text) {
    return false; // 强制返回 false，禁用 realtime 模式
}
// 文本分段 - 阿里云 TTS 限制 600 字符，增加到 550 留安全余量
function splitText(text, maxLength = 550) {
    const segments = [];
    let current = '';
    // 清理文本，移除可能导致问题的字符
    const cleanText = text.replace(/\r/g, '');
    // 按段落分割
    const paragraphs = cleanText.split(/\n+/);
    for (const para of paragraphs) {
        if ((current + para).length <= maxLength) {
            current += (current ? '\n' : '') + para;
        }
        else {
            if (current)
                segments.push(current);
            // 如果段落本身超长，按句子分割
            if (para.length > maxLength) {
                // 使用更安全的分割方式
                const sentences = para.match(/[^。！？；]+[。！？；]?/g) || [para];
                current = '';
                for (const sentence of sentences) {
                    if (sentence.length === 0)
                        continue;
                    if ((current + sentence).length <= maxLength) {
                        current += sentence;
                    }
                    else {
                        if (current)
                            segments.push(current);
                        // 如果句子本身超长，强制分割
                        if (sentence.length > maxLength) {
                            for (let i = 0; i < sentence.length; i += maxLength) {
                                segments.push(sentence.slice(i, i + maxLength));
                            }
                            current = '';
                        }
                        else {
                            current = sentence;
                        }
                    }
                }
            }
            else {
                current = para;
            }
        }
    }
    if (current)
        segments.push(current);
    // 验证每段长度不超过限制（安全检查）
    const safeLimit = 550;
    const validatedSegments = segments.map((seg, idx) => {
        if (seg.length > safeLimit) {
            console.warn(`⚠️ 段落 ${idx + 1} 长度 ${seg.length} 超过限制，强制截断`);
            return seg.substring(0, safeLimit);
        }
        return seg;
    });
    return validatedSegments;
}
// TTS Provider 工厂
function getTtsProvider(text, voiceId) {
    const useLongText = shouldUseLongText(text);
    // 文本超长时使用 WebSocket 实时合成
    if (useLongText && config_1.config.dashscope.apiKey) {
        console.log('🔊 文本超过5000字符，使用 Qwen Realtime TTS 流式合成');
        return { provider: new aliyun_realtime_provider_1.AliyunRealtimeTtsProvider(), type: 'realtime' };
    }
    // 优先使用阿里云百炼 Qwen TTS
    if (config_1.config.dashscope.apiKey) {
        console.log('🔊 使用阿里云百炼 Qwen TTS 服务');
        return { provider: new aliyun_provider_1.AliyunTtsProvider(), type: 'http' };
    }
    // 降级到模拟服务
    console.log('🔊 使用模拟 TTS 服务（无 API Key）');
    return { provider: new mock_provider_1.MockTtsProvider(), type: 'mock' };
}
// 随机选择 TTS 模型
function getRandomModel() {
    const models = config_1.config.dashscope.ttsModels;
    const model = models[Math.floor(Math.random() * models.length)];
    console.log(`🎲 随机选择 TTS 模型: ${model}`);
    return model;
}
// 生成音频（异步模式，不再创建 Audio 记录）
async function generateAudio(userId, text, voiceId, voiceParams, onComplete) {
    // 使用 UUID 作为音频 ID
    const audioId = (0, uuid_1.v4)();
    const audioDir = path_1.default.join(config_1.config.upload.dir, audioId);
    if (!fs_1.default.existsSync(audioDir)) {
        fs_1.default.mkdirSync(audioDir, { recursive: true });
    }
    console.log('📝 开始音频生成:', audioId, '文本长度:', text.length);
    // 异步处理音频生成
    processAudioGeneration(audioId, text, voiceId, voiceParams, audioDir, onComplete).catch(error => {
        const errMsg = `❌ 异步音频生成失败: ${error.message}`;
        console.error(errMsg);
        console.error('❌ 错误堆栈:', error.stack);
        logToFile(errMsg + '\n' + error.stack);
    });
    // 立即返回音频ID和状态（audioUrl 为空，生成完成后通过回调更新）
    return {
        audioId,
        audioUrl: '',
    };
}
/**
 * 异步处理音频生成
 */
async function processAudioGeneration(audioId, text, voiceId, voiceParams, audioDir, onComplete) {
    const logMsg = `🔄 开始处理音频 ID: ${audioId}, 文本长度: ${text.length}, voiceId: ${voiceId}`;
    console.log(logMsg);
    logToFile(logMsg);
    try {
        const { provider, type } = getTtsProvider(text, voiceId);
        console.log(`🔧 Provider type: ${type}, voiceName: ${voiceId}`);
        logToFile(`Provider type: ${type}, voiceId: ${voiceId}`);
        const voiceName = getAliyunVoice(voiceId);
        const selectedModel = (type !== 'mock') ? getRandomModel() : undefined;
        console.log(`🎲 selectedModel: ${selectedModel}`);
        // 根据 Provider 类型决定分段策略
        let segments;
        if (type === 'realtime') {
            segments = [text];
            console.log(`📝 使用 Qwen Realtime TTS，文本长度 ${text.length} 字符（不需分段）`);
        }
        else {
            segments = splitText(text);
            console.log(`📝 文本已分段: ${segments.length} 段`);
        }
        segments.forEach((seg, i) => {
            console.log(`   段落 ${i + 1}: ${seg.length} 字符`);
        });
        // 并行生成各段音频
        const audioFiles = [];
        const cloudUrls = [];
        const concurrency = type === 'realtime' ? 1 : 2;
        for (let i = 0; i < segments.length; i += concurrency) {
            const batch = segments.slice(i, i + concurrency);
            // 根据类型选择调用方式
            let results;
            if (type === 'mock') {
                results = await Promise.all(batch.map((segment, idx) => provider.synthesize(segment, voiceName, voiceParams, path_1.default.join(audioDir, `segment_${i + idx}.mp3`))));
            }
            else {
                results = await Promise.all(batch.map((segment, idx) => provider.synthesize(segment, voiceName, voiceParams, path_1.default.join(audioDir, `segment_${i + idx}.mp3`), 3, selectedModel)));
            }
            results.forEach(r => {
                if (r.startsWith('cloud:')) {
                    cloudUrls.push(r.substring(6));
                }
                else {
                    audioFiles.push(r);
                }
            });
        }
        console.log(`📁 生成了 ${audioFiles.length} 个音频文件, ${cloudUrls.length} 个云端URL`);
        let audioUrl = '';
        let duration = 0;
        let size = 0;
        if (cloudUrls.length > 0) {
            audioUrl = cloudUrls[0];
            console.log('☁️ 使用云端音频 URL:', audioUrl);
        }
        else if (audioFiles.length > 0) {
            const outputPath = path_1.default.join(audioDir, 'output.mp3');
            const mergedFile = await audio_merger_1.AudioMerger.merge(audioFiles, outputPath);
            const stats = fs_1.default.statSync(mergedFile);
            size = stats.size;
            duration = await audio_merger_1.AudioMerger.getDuration(mergedFile);
            audioUrl = `/uploads/${audioDir.split(/[/\\]/).pop()}/output.mp3`;
        }
        // 使用 AI 生成标题、摘要和标签
        console.log('🤖 使用 AI 生成标题、摘要和标签...');
        const [title, summary, tags] = await Promise.all([
            ai_summary_service_1.aiSummaryService.generateTitle(text),
            ai_summary_service_1.aiSummaryService.generateSummary(text, 200),
            ai_summary_service_1.aiSummaryService.extractTags(text),
        ]);
        const finalAudioUrl = cloudUrls.length > 0 ? cloudUrls[0] : audioUrl;
        // 调用完成回调（如果有）
        if (onComplete) {
            onComplete(finalAudioUrl, duration);
        }
        console.log('✅ 音频生成完成:', audioId, 'URL:', finalAudioUrl);
    }
    catch (error) {
        console.error('❌ processAudioGeneration 错误:', error);
        throw error;
    }
}
/**
 * 获取音频状态（已禁用，因为不再有 Audio 表）
 * TODO: 如需查询状态，需要实现基于文件系统的状态跟踪
 */
async function getAudioStatus(audioId) {
    // 由于 Audio 表已删除，暂时返回 not_found
    // 后续可以实现基于文件系统的状态跟踪
    return { status: 'not_found' };
}
// 获取可用音色
function getVoices() {
    return exports.VOICES;
}
