import chalk from "chalk";
import { selectConfig } from "./cli/config-selector.js";
import { createProjectTrustContext } from "./cli/project-trust.js";
import { APP_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
import { DefaultPackageManager } from "./core/package-manager.js";
import { resolveProjectTrusted } from "./core/project-trust.js";
import { DefaultResourceLoader } from "./core/resource-loader.js";
import { SettingsManager } from "./core/settings-manager.js";
import { hasProjectTrustInputs, ProjectTrustStore } from "./core/trust-manager.js";
import { spawnProcess } from "./utils/child-process.js";
import { getLatestPiRelease, isNewerPackageVersion } from "./utils/version-check.js";
import { cleanupWindowsSelfUpdateQuarantine, quarantineWindowsNativeDependencies, } from "./utils/windows-self-update.js";
import { getPackageCommandUsage, parsePackageCommand, printPackageCommandHelp } from "./package-manager-cli-parser.js";
function reportSettingsErrors(settingsManager, context) {
    const errors = settingsManager.drainErrors();
    for (const { scope, error } of errors) {
        console.error(chalk.yellow(`Warning (${context}, ${scope} settings): ${error.message}`));
        if (error.stack) {
            console.error(chalk.dim(error.stack));
        }
    }
}
function updateTargetIncludesSelf(target) {
    return target.type === "all" || target.type === "self";
}
function updateTargetIncludesExtensions(target) {
    return target.type === "all" || target.type === "extensions";
}
function printSelfUpdateUnavailable(npmCommand, updatePackageName = PACKAGE_NAME) {
    console.error(`error: ${APP_NAME} cannot self-update this installation.`);
    console.error(getSelfUpdateUnavailableInstruction(PACKAGE_NAME, npmCommand, updatePackageName));
    const entrypoint = process.argv[1];
    if (entrypoint) {
        console.error("");
        console.error(`Location of ${APP_NAME} executable: ${entrypoint}`);
    }
}
function printSelfUpdateFallback(command) {
    console.error(chalk.dim(`If this keeps failing, run this command yourself: ${command.display}`));
}
async function getSelfUpdatePlan(force) {
    if (force) {
        return { packageName: PACKAGE_NAME, shouldRun: true };
    }
    try {
        const latestRelease = await getLatestPiRelease();
        const packageName = latestRelease?.packageName ?? PACKAGE_NAME;
        if (!latestRelease || packageName !== PACKAGE_NAME || isNewerPackageVersion(latestRelease.version, VERSION)) {
            return { packageName, shouldRun: true };
        }
    }
    catch {
        return { packageName: PACKAGE_NAME, shouldRun: true };
    }
    console.log(chalk.green(`${APP_NAME} is already up to date (v${VERSION})`));
    return { packageName: PACKAGE_NAME, shouldRun: false };
}
async function runSelfUpdate(command) {
    console.log(chalk.dim(`Updating ${APP_NAME} with ${command.display}...`));
    for (const step of command.steps ?? [command]) {
        await new Promise((resolve, reject) => {
            const child = spawnProcess(step.command, step.args, {
                stdio: "inherit",
            });
            child.on("error", (error) => {
                reject(error);
            });
            child.on("close", (code, signal) => {
                if (code === 0) {
                    resolve();
                }
                else if (signal) {
                    reject(new Error(`${step.display} terminated by signal ${signal}`));
                }
                else {
                    reject(new Error(`${step.display} exited with code ${code ?? "unknown"}`));
                }
            });
        });
    }
}
function prepareWindowsNpmSelfUpdate() {
    if (process.platform !== "win32") {
        return;
    }
    const packageDir = getPackageDir();
    cleanupWindowsSelfUpdateQuarantine(packageDir);
    quarantineWindowsNativeDependencies(packageDir);
}
function parseProjectTrustOverride(args) {
    let trustOverride;
    for (const arg of args) {
        if (arg === "--approve" || arg === "-a") {
            trustOverride = true;
        }
        else if (arg === "--no-approve" || arg === "-na") {
            trustOverride = false;
        }
    }
    return trustOverride;
}
function getCommandAppMode() {
    return process.stdin.isTTY && process.stdout.isTTY ? "interactive" : "print";
}
function reportProjectTrustWarnings(warnings) {
    for (const warning of warnings) {
        console.error(chalk.yellow(`Warning: ${warning}`));
    }
}
async function createCommandSettingsManager(options) {
    const settingsManager = SettingsManager.create(options.cwd, options.agentDir, { projectTrusted: false });
    const projectTrustWarnings = [];
    const appMode = getCommandAppMode();
    const extensionsResult = options.projectTrustOverride === undefined && hasProjectTrustInputs(options.cwd)
        ? await new DefaultResourceLoader({
            cwd: options.cwd,
            agentDir: options.agentDir,
            settingsManager,
            extensionFactories: options.extensionFactories,
        }).loadProjectTrustExtensions()
        : undefined;
    for (const error of extensionsResult?.errors ?? []) {
        projectTrustWarnings.push(`Failed to load extension "${error.path}": ${error.error}`);
    }
    const projectTrusted = await resolveProjectTrusted({
        cwd: options.cwd,
        trustStore: new ProjectTrustStore(options.agentDir),
        trustOverride: options.projectTrustOverride,
        defaultProjectTrust: settingsManager.getDefaultProjectTrust(),
        extensionsResult,
        projectTrustContext: createProjectTrustContext({
            cwd: options.cwd,
            mode: appMode,
            settingsManager,
            hasUI: appMode === "interactive",
        }),
        onExtensionError: (message) => projectTrustWarnings.push(message),
    });
    settingsManager.setProjectTrusted(projectTrusted);
    return { settingsManager, projectTrustWarnings };
}
export async function handleConfigCommand(args, runtimeOptions = {}) {
    if (args[0] !== "config") {
        return false;
    }
    const cwd = process.cwd();
    const agentDir = getAgentDir();
    const { settingsManager, projectTrustWarnings } = await createCommandSettingsManager({
        cwd,
        agentDir,
        projectTrustOverride: parseProjectTrustOverride(args),
        extensionFactories: runtimeOptions.extensionFactories,
    });
    reportProjectTrustWarnings(projectTrustWarnings);
    reportSettingsErrors(settingsManager, "config command");
    const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
    const resolvedPaths = await packageManager.resolve();
    await selectConfig({
        resolvedPaths,
        settingsManager,
        cwd,
        agentDir,
    });
    process.exit(0);
}
export async function handlePackageCommand(args, runtimeOptions = {}) {
    const options = parsePackageCommand(args);
    if (!options) {
        return false;
    }
    if (options.help) {
        printPackageCommandHelp(options.command);
        return true;
    }
    if (options.invalidOption) {
        console.error(chalk.red(`Unknown option ${options.invalidOption} for "${options.command}".`));
        console.error(chalk.dim(`Use "${APP_NAME} --help" or "${getPackageCommandUsage(options.command)}".`));
        process.exitCode = 1;
        return true;
    }
    if (options.missingOptionValue) {
        console.error(chalk.red(`Missing value for ${options.missingOptionValue}.`));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    if (options.invalidArgument) {
        console.error(chalk.red(`Unexpected argument ${options.invalidArgument}.`));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    if (options.conflictingOptions) {
        console.error(chalk.red(options.conflictingOptions));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    const source = options.source;
    if ((options.command === "install" || options.command === "remove") && !source) {
        console.error(chalk.red(`Missing ${options.command} source.`));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    const cwd = process.cwd();
    const agentDir = getAgentDir();
    const writesProjectPackageConfig = (options.command === "install" || options.command === "remove") && options.local;
    const { settingsManager, projectTrustWarnings } = await createCommandSettingsManager({
        cwd,
        agentDir,
        projectTrustOverride: options.projectTrustOverride,
        extensionFactories: runtimeOptions.extensionFactories,
    });
    reportProjectTrustWarnings(projectTrustWarnings);
    if (!settingsManager.isProjectTrusted() && writesProjectPackageConfig) {
        console.error(chalk.red("Project is not trusted. Use --approve to modify local package config."));
        process.exitCode = 1;
        return true;
    }
    reportSettingsErrors(settingsManager, "package command");
    const selfUpdateNpmCommand = settingsManager.getGlobalSettings().npmCommand;
    const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
    packageManager.setProgressCallback((event) => {
        if (event.type === "start") {
            process.stdout.write(chalk.dim(`${event.message}\n`));
        }
    });
    try {
        switch (options.command) {
            case "install":
                await packageManager.installAndPersist(source, { local: options.local });
                console.log(chalk.green(`Installed ${source}`));
                return true;
            case "remove": {
                const removed = await packageManager.removeAndPersist(source, { local: options.local });
                if (!removed) {
                    console.error(chalk.red(`No matching package found for ${source}`));
                    process.exitCode = 1;
                    return true;
                }
                console.log(chalk.green(`Removed ${source}`));
                return true;
            }
            case "list": {
                const configuredPackages = packageManager.listConfiguredPackages();
                const userPackages = configuredPackages.filter((pkg) => pkg.scope === "user");
                const projectPackages = configuredPackages.filter((pkg) => pkg.scope === "project");
                if (configuredPackages.length === 0) {
                    console.log(chalk.dim("No packages installed."));
                    return true;
                }
                const formatPackage = (pkg) => {
                    const display = pkg.filtered ? `${pkg.source} (filtered)` : pkg.source;
                    console.log(`  ${display}`);
                    if (pkg.installedPath) {
                        console.log(chalk.dim(`    ${pkg.installedPath}`));
                    }
                };
                if (userPackages.length > 0) {
                    console.log(chalk.bold("User packages:"));
                    for (const pkg of userPackages) {
                        formatPackage(pkg);
                    }
                }
                if (projectPackages.length > 0) {
                    if (userPackages.length > 0)
                        console.log();
                    console.log(chalk.bold("Project packages:"));
                    for (const pkg of projectPackages) {
                        formatPackage(pkg);
                    }
                }
                return true;
            }
            case "update": {
                const target = options.updateTarget ?? { type: "self" };
                if (options.showExtensionsSkippedNote) {
                    console.log(chalk.dim(`Extensions are skipped. Run ${APP_NAME} update --extensions to update extensions.`));
                }
                if (updateTargetIncludesExtensions(target)) {
                    const updateSource = target.type === "extensions" ? target.source : undefined;
                    await packageManager.update(updateSource);
                    if (updateSource) {
                        console.log(chalk.green(`Updated ${updateSource}`));
                    }
                    else {
                        console.log(chalk.green("Updated packages"));
                    }
                }
                if (updateTargetIncludesSelf(target)) {
                    const selfUpdatePlan = await getSelfUpdatePlan(options.force);
                    if (!selfUpdatePlan.shouldRun) {
                        return true;
                    }
                    const installMethod = detectInstallMethod();
                    if (process.platform === "win32" && installMethod !== "npm" && installMethod !== "pnpm") {
                        console.error(chalk.red(`${APP_NAME} self-update on Windows is only supported for npm and pnpm installs.`));
                        console.error(chalk.dim(`Detected install method: ${installMethod}. Update ${APP_NAME} manually.`));
                        process.exitCode = 1;
                        return true;
                    }
                    const selfUpdateCommand = getSelfUpdateCommand(PACKAGE_NAME, selfUpdateNpmCommand, selfUpdatePlan.packageName);
                    if (!selfUpdateCommand) {
                        printSelfUpdateUnavailable(selfUpdateNpmCommand, selfUpdatePlan.packageName);
                        process.exitCode = 1;
                        return true;
                    }
                    try {
                        if (installMethod === "npm") {
                            prepareWindowsNpmSelfUpdate();
                        }
                        await runSelfUpdate(selfUpdateCommand);
                    }
                    catch (error) {
                        const message = error instanceof Error ? error.message : "Unknown package command error";
                        console.error(chalk.red(`Error: ${message}`));
                        printSelfUpdateFallback(selfUpdateCommand);
                        process.exitCode = 1;
                        return true;
                    }
                    console.log(chalk.green(`Updated ${APP_NAME}`));
                }
                return true;
            }
        }
    }
    catch (error) {
        const message = error instanceof Error ? error.message : "Unknown package command error";
        console.error(chalk.red(`Error: ${message}`));
        process.exitCode = 1;
        return true;
    }
}
//# sourceMappingURL=package-manager-cli.js.map