import { execFile } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
let resolvedFfmpeg;
function canonicalDirs(platform) {
    if (platform === 'darwin')
        return ['/opt/homebrew/bin', '/usr/local/bin'];
    if (platform === 'linux')
        return ['/usr/local/bin', '/usr/bin', '/snap/bin'];
    return [];
}
function pathCandidates(env, platform, platformDirs) {
    const names = platform === 'win32' ? ['ffmpeg.exe'] : ['ffmpeg'];
    const dirs = [...new Set([...(env.PATH ?? '').split(path.delimiter).filter(Boolean), ...platformDirs])];
    return dirs.flatMap((dir) => names.map((name) => path.join(dir, name)));
}
function managedCandidates(home, platform) {
    const name = platform === 'win32' ? 'ffmpeg.exe' : 'ffmpeg';
    return [
        path.join(home, '.agents', '.cache', 'bin', name),
        path.join(home, '.agents', '.cache', 'ffmpeg', 'bin', name),
        path.join(home, '.agents', '.cache', 'ffmpeg', 'node_modules', 'ffmpeg-static', name),
    ];
}
async function isUsable(binary) {
    try {
        await execFileAsync(binary, ['-version'], { timeout: 10_000, windowsHide: true });
        return true;
    }
    catch {
        return false;
    }
}
async function firstUsable(candidates) {
    for (const candidate of candidates) {
        if (fs.existsSync(candidate) && await isUsable(candidate))
            return candidate;
    }
    return undefined;
}
function commandOnPath(name, env, platform, platformDirs) {
    const names = platform === 'win32' ? [`${name}.exe`, `${name}.cmd`, name] : [name];
    const dirs = [...new Set([...(env.PATH ?? '').split(path.delimiter).filter(Boolean), ...platformDirs])];
    for (const dir of dirs) {
        for (const candidate of names.map((entry) => path.join(dir, entry))) {
            try {
                fs.accessSync(candidate, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
                return candidate;
            }
            catch { /* keep looking */ }
        }
    }
    return undefined;
}
export function ffmpegManualCommand(platform, osRelease = '') {
    if (platform === 'darwin')
        return 'brew install ffmpeg';
    if (platform === 'win32')
        return 'winget install Gyan.FFmpeg';
    if (/\b(ID|ID_LIKE)=(?:"[^"]*\b)?(?:ubuntu|debian)\b/m.test(osRelease)) {
        return 'sudo apt-get update && sudo apt-get install -y ffmpeg';
    }
    return 'install ffmpeg with your system package manager';
}
function readOsRelease() {
    try {
        return fs.readFileSync('/etc/os-release', 'utf8');
    }
    catch {
        return '';
    }
}
async function installManagedFfmpeg(home, env, platform, platformDirs) {
    const npm = commandOnPath('npm', env, platform, platformDirs);
    if (!npm)
        return undefined;
    const prefix = path.join(home, '.agents', '.cache', 'ffmpeg');
    await fs.promises.mkdir(prefix, { recursive: true });
    await execFileAsync(npm, [
        'install', '--prefix', prefix, '--no-save', '--no-audit', '--no-fund',
        'ffmpeg-static@5.2.0',
    ], { env, timeout: 10 * 60_000, windowsHide: true });
    return firstUsable(managedCandidates(home, platform));
}
function installAttempt(platform, env, osRelease, platformDirs) {
    if (platform === 'darwin') {
        const brew = commandOnPath('brew', env, platform, platformDirs);
        return brew ? [{ command: brew, args: ['install', 'ffmpeg'] }] : undefined;
    }
    if (platform === 'win32') {
        const winget = commandOnPath('winget', env, platform, platformDirs);
        return winget ? [{ command: winget, args: ['install', '--id', 'Gyan.FFmpeg', '--exact', '--accept-source-agreements', '--accept-package-agreements'] }] : undefined;
    }
    if (platform !== 'linux')
        return undefined;
    if (/\b(ID|ID_LIKE)=(?:"[^"]*\b)?(?:ubuntu|debian)\b/m.test(osRelease)) {
        const apt = commandOnPath('apt-get', env, platform, platformDirs);
        if (!apt)
            return undefined;
        if (typeof process.getuid === 'function' && process.getuid() === 0) {
            return [
                { command: apt, args: ['update'] },
                { command: apt, args: ['install', '-y', 'ffmpeg'] },
            ];
        }
        const sudo = commandOnPath('sudo', env, platform, platformDirs);
        return sudo ? [
            { command: sudo, args: ['-n', apt, 'update'] },
            { command: sudo, args: ['-n', apt, 'install', '-y', 'ffmpeg'] },
        ] : undefined;
    }
    const managers = [
        ['apk', ['add', 'ffmpeg']],
        ['dnf', ['install', '-y', 'ffmpeg']],
        ['yum', ['install', '-y', 'ffmpeg']],
        ['pacman', ['-S', '--noconfirm', 'ffmpeg']],
        ['zypper', ['--non-interactive', 'install', 'ffmpeg']],
    ];
    for (const [name, args] of managers) {
        const command = commandOnPath(name, env, platform, platformDirs);
        if (command)
            return [{ command, args }];
    }
    return undefined;
}
export async function resolveFfmpeg(options = {}) {
    if (resolvedFfmpeg)
        return resolvedFfmpeg;
    resolvedFfmpeg = (async () => {
        const env = options.env ?? process.env;
        const home = options.home ?? os.homedir();
        const platform = options.platform ?? os.platform();
        const osRelease = options.osRelease ?? readOsRelease();
        const executableDirs = options.executableDirs ?? canonicalDirs(platform);
        const candidates = [...pathCandidates(env, platform, executableDirs), ...managedCandidates(home, platform)];
        const existing = await firstUsable(candidates);
        if (existing)
            return existing;
        const attempt = installAttempt(platform, env, osRelease, executableDirs);
        let failure = 'no supported automatic installer was found';
        if (attempt) {
            try {
                for (const step of attempt) {
                    await execFileAsync(step.command, step.args, {
                        env,
                        timeout: 10 * 60_000,
                        windowsHide: true,
                    });
                }
                const installed = await firstUsable([...pathCandidates(env, platform, executableDirs), ...managedCandidates(home, platform)]);
                if (installed)
                    return installed;
                failure = `${path.basename(attempt.at(-1).command)} completed but ffmpeg is still unavailable`;
            }
            catch (error) {
                failure = `${path.basename(attempt.at(-1).command)} failed: ${error.message}`;
            }
        }
        try {
            const managed = await installManagedFfmpeg(home, env, platform, executableDirs);
            if (managed)
                return managed;
            failure += '; managed npm installation was unavailable';
        }
        catch (error) {
            failure += `; managed npm installation failed: ${error.message}`;
        }
        const manual = ffmpegManualCommand(platform, osRelease);
        throw new Error(`ffmpeg is required for browser recording and automatic installation failed (${failure}). Run \`${manual}\`, then retry.`);
    })();
    try {
        return await resolvedFfmpeg;
    }
    catch (error) {
        resolvedFfmpeg = undefined;
        throw error;
    }
}
export function resetFfmpegResolverForTest() {
    resolvedFfmpeg = undefined;
}
