import { execFile } from 'node:child_process';
import { createHash } from 'node:crypto';
import { existsSync, readFileSync } from 'node:fs';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { promisify } from 'node:util';
// ESM shim — __filename is unavailable under "type": "module".
const __filename = fileURLToPath(import.meta.url);
const execFileAsync = promisify(execFile);
const DEFAULT_UPDATE_MANIFEST = 'https://extension.vigthoria.io/downloads/manifest.json';
const TRUSTED_RELEASE_HOSTS = new Set([
    'extension.vigthoria.io',
    'landing.vigthoria.io',
    'cli.vigthoria.io',
    'coder.vigthoria.io',
    'market.vigthoria.io',
]);
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}`);
    }
}
function validateTrustedHttpsUrl(value, label) {
    let parsed;
    try {
        parsed = new URL(value);
    }
    catch {
        throw new Error(`${label} is not a valid URL`);
    }
    if (parsed.protocol !== 'https:' || !TRUSTED_RELEASE_HOSTS.has(parsed.hostname)) {
        throw new Error(`${label} must use HTTPS on an approved Vigthoria release host`);
    }
    return parsed;
}
async function getStableRelease() {
    const manifestUrl = process.env.VIGTHORIA_UPDATE_MANIFEST_URL
        || DEFAULT_UPDATE_MANIFEST;
    validateTrustedHttpsUrl(manifestUrl, 'Update manifest URL');
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 20_000);
    try {
        const response = await fetch(manifestUrl, {
            headers: { Accept: 'application/json' },
            redirect: 'follow',
            signal: controller.signal,
        });
        if (!response.ok) {
            throw new Error(`manifest returned HTTP ${response.status}`);
        }
        validateTrustedHttpsUrl(response.url || manifestUrl, 'Resolved manifest URL');
        const document = await response.json();
        const stable = document.channels?.stable;
        const version = String(stable?.version || '').trim();
        const url = String(stable?.url || '').trim();
        const sha256 = String(stable?.sha256 || '').trim().toLowerCase();
        if (!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/.test(version)) {
            throw new Error('stable manifest version is invalid');
        }
        validateTrustedHttpsUrl(url, 'Stable release URL');
        if (!/^[a-f0-9]{64}$/.test(sha256)) {
            throw new Error('stable release SHA-256 is missing or invalid');
        }
        return { version, url, sha256 };
    }
    finally {
        clearTimeout(timeout);
    }
}
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.');
    }
    let release;
    let latest;
    try {
        release = await getStableRelease();
        latest = release.version;
    }
    catch (manifestError) {
        const reason = manifestError instanceof Error
            ? manifestError.message
            : String(manifestError);
        console.warn(`Verified release manifest unavailable (${reason}); checking npm.`);
        latest = await getLatestVersion(packageName);
    }
    return {
        current,
        latest,
        packageName,
        updateAvailable: compareVersions(current, latest) < 0,
        release,
    };
}
function resolveInstallTargetCommand(packageManager, target, installGlobal) {
    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 resolveInstallCommand(packageManager, packageName, installGlobal) {
    return resolveInstallTargetCommand(packageManager, `${packageName}@latest`, installGlobal);
}
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);
}
async function downloadVerifiedRelease(release) {
    const directory = await mkdtemp(path.join(tmpdir(), 'vigthoria-cli-update-'));
    const archive = path.join(directory, `vigthoria-cli-${release.version}.tgz`);
    try {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 60_000);
        let response;
        try {
            response = await fetch(release.url, {
                redirect: 'follow',
                signal: controller.signal,
            });
        }
        finally {
            clearTimeout(timeout);
        }
        if (!response.ok) {
            throw new Error(`release download returned HTTP ${response.status}`);
        }
        validateTrustedHttpsUrl(response.url || release.url, 'Resolved release URL');
        const bytes = Buffer.from(await response.arrayBuffer());
        const digest = createHash('sha256').update(bytes).digest('hex');
        if (digest !== release.sha256) {
            throw new Error(`release checksum mismatch (expected ${release.sha256}, received ${digest})`);
        }
        await writeFile(archive, bytes, { mode: 0o600 });
        return { directory, archive };
    }
    catch (error) {
        await rm(directory, { recursive: true, force: true });
        throw error;
    }
}
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;
        if (info.release) {
            console.log(`Installing checksum-verified ${info.packageName}@${info.release.version} `
                + `with ${packageManager}...`);
            const downloaded = await downloadVerifiedRelease(info.release);
            try {
                const installCommand = resolveInstallTargetCommand(packageManager, downloaded.archive, installGlobal);
                await execPackageManager(installCommand.command, installCommand.args, 120_000);
            }
            finally {
                await rm(downloaded.directory, { recursive: true, force: true });
            }
            console.log('Verified release installation finished successfully.');
        }
        else {
            console.log(`Installing ${info.packageName}@latest with ${packageManager}...`);
            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;
