import { execFile } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { promisify } from 'node:util';
import { installUpdateWindows } from '../utils/tools.js';
// ESM shim — __filename is unavailable under "type": "module".
const __filename = fileURLToPath(import.meta.url);
const execFileAsync = promisify(execFile);
function markSuccessExit() {
    process.exitCode = 0;
}
function markErrorExit() {
    process.exitCode = 1;
}
function findPackageJson(startDir) {
    let currentDir = startDir;
    for (let depth = 0; depth < 8; depth += 1) {
        const candidate = path.join(currentDir, 'package.json');
        if (existsSync(candidate)) {
            return candidate;
        }
        const parentDir = path.join(currentDir, '..');
        if (parentDir === currentDir) {
            break;
        }
        currentDir = parentDir;
    }
    return null;
}
function readOwnPackageJson() {
    const currentFile = __filename;
    const packagePath = findPackageJson(path.join(currentFile, '..'));
    if (!packagePath) {
        throw new Error('Unable to locate package.json for the Vigthoria CLI.');
    }
    try {
        return JSON.parse(readFileSync(packagePath, 'utf8'));
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        throw new Error(`Unable to read package metadata: ${message}`);
    }
}
function compareVersions(a, b) {
    const parse = (version) => version.replace(/^v/, '').split(/[.-]/).map((part) => {
        const value = Number.parseInt(part, 10);
        return Number.isNaN(value) ? 0 : value;
    });
    const left = parse(a);
    const right = parse(b);
    const length = Math.max(left.length, right.length);
    for (let index = 0; index < length; index += 1) {
        const leftValue = left[index] ?? 0;
        const rightValue = right[index] ?? 0;
        if (leftValue > rightValue)
            return 1;
        if (leftValue < rightValue)
            return -1;
    }
    return 0;
}
function quoteCmdArg(value) {
    if (!/[\s"&()<>^|]/.test(value)) {
        return value;
    }
    return `"${value.replace(/(\\*)"/g, '$1$1\\"')}"`;
}
function toPlatformCommand(command, args) {
    if (process.platform !== 'win32') {
        return { command, args };
    }
    const cmdPath = process.env.ComSpec || path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'cmd.exe');
    return {
        command: cmdPath,
        args: ['/d', '/s', '/c', [command, ...args].map(quoteCmdArg).join(' ')],
    };
}
async function execPackageManager(command, args, timeout) {
    const platformCommand = toPlatformCommand(command, args);
    const { stdout } = await execFileAsync(platformCommand.command, platformCommand.args, {
        timeout,
        maxBuffer: 1024 * 1024,
        windowsHide: true,
    });
    return stdout;
}
async function getLatestVersion(packageName) {
    try {
        const stdout = await execPackageManager('npm', ['view', packageName, 'version'], 30_000);
        const latest = stdout.trim();
        if (!latest) {
            throw new Error('npm returned an empty version response');
        }
        return latest;
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        throw new Error(`Unable to check the latest published version: ${message}`);
    }
}
async function getVersionInfo() {
    const packageJson = readOwnPackageJson();
    const packageName = packageJson.name;
    const current = packageJson.version;
    if (!packageName || !current) {
        throw new Error('The CLI package metadata must include both name and version.');
    }
    const latest = await getLatestVersion(packageName);
    return {
        current,
        latest,
        packageName,
        updateAvailable: compareVersions(current, latest) < 0,
    };
}
function resolveInstallCommand(packageManager, packageName, installGlobal) {
    const target = `${packageName}@latest`;
    switch (packageManager) {
        case 'pnpm':
            return { command: 'pnpm', args: installGlobal ? ['add', '--global', target] : ['add', '-D', target] };
        case 'yarn':
            return { command: 'yarn', args: installGlobal ? ['global', 'add', target] : ['add', '--dev', target] };
        case 'bun':
            return { command: 'bun', args: installGlobal ? ['add', '--global', target] : ['add', '--dev', target] };
        case 'npm':
        default:
            return { command: 'npm', args: installGlobal ? ['install', '--global', target] : ['install', '--save-dev', target] };
    }
}
function formatCommand(command) {
    return [command.command, ...command.args].map(quoteCmdArg).join(' ');
}
async function runInstall(packageManager, packageName, installGlobal) {
    const installCommand = resolveInstallCommand(packageManager, packageName, installGlobal);
    await execPackageManager(installCommand.command, installCommand.args, 120_000);
}
export async function updateCommand(options = {}) {
    try {
        const info = await getVersionInfo();
        console.log(`Vigthoria CLI current version: ${info.current}`);
        console.log(`Vigthoria CLI latest version:  ${info.latest}`);
        if (!info.updateAvailable) {
            console.log('You are already running the latest Vigthoria CLI.');
            markSuccessExit();
            return;
        }
        console.log(`Update available for ${info.packageName}: ${info.current} → ${info.latest}`);
        if (options.check) {
            markSuccessExit();
            return;
        }
        const packageManager = options.packageManager ?? 'npm';
        const installGlobal = options.global ?? true;
        console.log(`Installing ${info.packageName}@latest with ${packageManager}...`);
        if (process.platform === 'win32') {
            const installerResult = await installUpdateWindows();
            if (!installerResult.success && installerResult.error === 'ENOENT') {
                const fallbackCommand = resolveInstallCommand(packageManager, info.packageName, installGlobal);
                console.warn('Windows update installer was not found (ENOENT).');
                console.warn('The bundled Windows installer may be missing from this installation.');
                console.warn(`Manual installation command: ${formatCommand(fallbackCommand)}`);
                console.warn('If automatic fallback fails, run the command above manually or download the latest Windows installer from the Vigthoria release page.');
                await execPackageManager(fallbackCommand.command, fallbackCommand.args, 120_000);
                console.log('Package manager installation finished successfully.');
            }
            else if (!installerResult.success) {
                throw new Error(installerResult.error || 'Windows installer failed to start.');
            }
            else {
                console.log('Windows installer started successfully. Complete the installer prompts to finish updating.');
            }
        }
        else {
            await runInstall(packageManager, info.packageName, installGlobal);
            console.log('Package manager installation finished successfully.');
        }
        console.log('Vigthoria CLI update complete.');
        markSuccessExit();
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        console.error(`Update failed: ${message}`);
        markErrorExit();
    }
}
export async function update() {
    try {
        await updateCommand();
        if (process.exitCode === 1) {
            return;
        }
        markSuccessExit();
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        console.error(`Update failed: ${message}`);
        markErrorExit();
    }
}
export default updateCommand;
