import { execFile } from 'child_process';
import { promises as fs } from 'fs';
import os from 'os';
import path from 'path';
import { promisify } from 'util';

const execFileAsync = promisify(execFile);

/**
 * 使用 ffmpeg 从视频二进制中提取第一帧，返回 JPEG buffer
 */
export async function extractFirstFrame(videoBuffer: Buffer): Promise<{ buffer: Buffer; contentType: string; ext: string } | null> {
    const ffmpegCmd = process.env.FFMPEG_PATH || 'ffmpeg';
    const tmpDir = os.tmpdir();
    const base = `dy_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const inPath = path.join(tmpDir, `${base}.mp4`);
    const outPath = path.join(tmpDir, `${base}.jpg`);

    try {
        await fs.writeFile(inPath, videoBuffer);
        const args = [
            '-hide_banner',
            '-loglevel', 'error',
            '-ss', '0',
            '-i', inPath,
            '-frames:v', '1',
            '-q:v', '2',
            '-f', 'image2',
            '-y',
            outPath,
        ];
        await execFileAsync(ffmpegCmd, args, { windowsHide: true });
        const img = await fs.readFile(outPath);
        return { buffer: img, contentType: 'image/jpeg', ext: 'jpg' };
    } catch (e: any) {
        if (e && (e.code === 'ENOENT' || /not found|is not recognized/i.test(String(e.message)))) {
            console.warn('系统未检测到 ffmpeg，可安装并配置 PATH 或设置 FFMPEG_PATH 后启用封面提取。');
            return null;
        }
        throw e;
    } finally {
        try { await fs.unlink(inPath); } catch { }
        try { await fs.unlink(outPath); } catch { }
    }
}

/**
 * 使用 ffprobe 获取视频时长（毫秒）
 */
export async function getVideoDuration(videoBuffer: Buffer): Promise<number | null> {
    const ffprobeCmd = process.env.FFPROBE_PATH || 'ffprobe';
    const tmpDir = os.tmpdir();
    const base = `dy_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const inPath = path.join(tmpDir, `${base}.mp4`);

    try {
        await fs.writeFile(inPath, videoBuffer);
        const args = [
            '-v', 'error',
            '-show_entries', 'format=duration',
            '-of', 'default=noprint_wrappers=1:nokey=1',
            inPath,
        ];
        const { stdout } = await execFileAsync(ffprobeCmd, args, { windowsHide: true });
        const durationSeconds = parseFloat(stdout.trim());
        if (isNaN(durationSeconds)) return null;
        return Math.round(durationSeconds * 1000); // 转换为毫秒
    } catch (e: any) {
        if (e && (e.code === 'ENOENT' || /not found|is not recognized/i.test(String(e.message)))) {
            console.warn('系统未检测到 ffprobe，可安装并配置 PATH 或设置 FFPROBE_PATH 后启用时长提取。');
            return null;
        }
        console.warn(`获取视频时长失败: ${e?.message || e}`);
        return null;
    } finally {
        try { await fs.unlink(inPath); } catch { }
    }
}

/**
 * 使用 ffmpeg 将视频二进制转换为音频，返回音频 Buffer
 *
 * 默认输出 mp3，可通过 opts.format 指定 'mp3' | 'aac' | 'wav'
 * - mp3: 使用 libmp3lame，contentType 为 'audio/mpeg'
 * - aac: 输出 m4a 容器（AAC 编码），contentType 为 'audio/mp4'
 * - wav: PCM 16-bit，44100Hz，双声道，contentType 为 'audio/wav'
 *
 * 若系统未安装 ffmpeg 或未在 PATH 中，返回 null 并给出提示。
 */
export async function extractAudio(
    videoBuffer: Buffer,
    opts?: { format?: 'mp3' | 'aac' | 'wav'; bitrateKbps?: number }
): Promise<{ buffer: Buffer; contentType: string; ext: string } | null> {
    const ffmpegCmd = process.env.FFMPEG_PATH || 'ffmpeg';
    const tmpDir = os.tmpdir();
    const base = `dy_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const inPath = path.join(tmpDir, `${base}.mp4`);

    const format = opts?.format ?? 'mp3';
    const bitrate = Math.max(32, Math.min(512, opts?.bitrateKbps ?? 192)); // 安全范围 32~512 kbps

    // 根据目标格式设置输出路径、MIME 与编码参数
    let outPath = '';
    let contentType = '';
    let ext = '';
    let codecArgs: string[] = [];

    if (format === 'mp3') {
        ext = 'mp3';
        contentType = 'audio/mpeg';
        outPath = path.join(tmpDir, `${base}.${ext}`);
        codecArgs = ['-c:a', 'libmp3lame', '-b:a', `${bitrate}k`];
    } else if (format === 'aac') {
        // 使用 m4a 容器更通用
        ext = 'm4a';
        contentType = 'audio/mp4';
        outPath = path.join(tmpDir, `${base}.${ext}`);
        codecArgs = ['-c:a', 'aac', '-b:a', `${bitrate}k`, '-movflags', '+faststart'];
    } else {
        // wav
        ext = 'wav';
        contentType = 'audio/wav';
        outPath = path.join(tmpDir, `${base}.${ext}`);
        codecArgs = ['-f', 'wav', '-acodec', 'pcm_s16le', '-ar', '44100', '-ac', '2'];
    }

    try {
        await fs.writeFile(inPath, videoBuffer);
        const args = [
            '-hide_banner',
            '-loglevel', 'error',
            '-i', inPath,
            '-vn', // 丢弃视频流
            ...codecArgs,
            '-y',
            outPath,
        ];
        await execFileAsync(ffmpegCmd, args, { windowsHide: true });
        const audio = await fs.readFile(outPath);
        return { buffer: audio, contentType, ext };
    } catch (e: any) {
        if (e && (e.code === 'ENOENT' || /not found|is not recognized/i.test(String(e.message)))) {
            console.warn('系统未检测到 ffmpeg，可安装并配置 PATH 或设置 FFMPEG_PATH 后启用音频提取。');
            return null;
        }
        // 一些环境可能缺少特定编码器（如 libmp3lame），提示并抛出原始错误
        console.warn(`提取音频失败: ${e?.message || e}`);
        throw e;
    } finally {
        try { await fs.unlink(inPath); } catch { }
        try {
            if (outPath) await fs.unlink(outPath);
        } catch { }
    }
}
