import { spawnProcess, spawnProcessSync } from "../utils/child-process.js";
import { resolvePath as resolveAbsolutePath } from "../utils/paths.js";
import { isStdoutTakenOver } from "./output-guard.js";
import { getCommandEnv } from "./package-manager-env.js";
import { getLocalGitUpdateTarget as getLocalGitUpdateTargetFromContext } from "./package-manager-git.js";
import { getGitInstallPath as getGitInstallPathFromContext, } from "./package-manager-paths.js";
import { getGlobalNpmRoot as getGlobalNpmRootFromContext, getLatestNpmVersion as getLatestNpmVersionFromContext, } from "./package-manager-npm.js";
import { checkForAvailableUpdates as checkForAvailableUpdatesFromContext, install as installFromContext, installParsedSource as installParsedSourceFromContext, remove as removeFromContext, update as updateFromContext, } from "./package-manager-operations.js";
import { resolveExtensionSources as resolveExtensionSourcesFromContext, resolvePackages, } from "./package-manager-resolver.js";
import { addSourceToSettings as addSourceToSettingsInContext, getInstalledPath as getInstalledPathFromContext, listConfiguredPackages as listConfiguredPackagesFromContext, removeSourceFromSettings as removeSourceFromSettingsInContext, } from "./package-manager-settings.js";
import { getPackageIdentity as getPackageIdentityFromContext, parseSource as parsePackageSource, } from "./package-manager-source.js";
import { gitHasAvailableUpdate as gitHasAvailableUpdateFromContext, refreshTemporaryGitSource as refreshTemporaryGitSourceFromContext, updateGit as updateGitFromContext, } from "./package-manager-git.js";
export class DefaultPackageManager {
    constructor(options) {
        this.context = {
            cwd: resolveAbsolutePath(options.cwd),
            agentDir: resolveAbsolutePath(options.agentDir),
            settingsManager: options.settingsManager,
        };
        this.context.driver = {
            runCommand: (command, args, runOptions) => this.runCommand(command, args, runOptions),
            runCommandCapture: (command, args, runOptions) => this.runCommandCapture(command, args, runOptions),
            runCommandSync: (command, args) => this.runCommandSync(command, args),
            installParsedSource: (parsed, scope) => this.installParsedSource(parsed, scope),
            updateGit: (source, scope) => this.updateGit(source, scope),
            gitHasAvailableUpdate: (installedPath) => this.gitHasAvailableUpdate(installedPath),
            refreshTemporaryGitSource: (source, sourceStr) => this.refreshTemporaryGitSource(source, sourceStr),
            getLocalGitUpdateTarget: (installedPath) => this.getLocalGitUpdateTarget(installedPath),
            getGlobalNpmRoot: () => this.getGlobalNpmRoot(),
            parseSource: (source) => this.parseSource(source),
            getPackageIdentity: (source, scope) => this.getPackageIdentity(source, scope),
            getGitInstallPath: (source, scope) => this.getGitInstallPath(source, scope),
            getLatestNpmVersion: (packageSpec, range) => this.getLatestNpmVersion(packageSpec, range),
        };
    }
    setProgressCallback(callback) {
        this.context.progressCallback = callback;
    }
    addSourceToSettings(source, options) {
        return addSourceToSettingsInContext(this.context, source, options);
    }
    removeSourceFromSettings(source, options) {
        return removeSourceFromSettingsInContext(this.context, source, options);
    }
    getInstalledPath(source, scope) {
        return getInstalledPathFromContext(this.context, source, scope);
    }
    async resolve(onMissing) {
        return resolvePackages(this.context, onMissing);
    }
    async resolveExtensionSources(sources, options) {
        return resolveExtensionSourcesFromContext(this.context, sources, options);
    }
    listConfiguredPackages() {
        return listConfiguredPackagesFromContext(this.context);
    }
    async install(source, options) {
        await installFromContext(this.context, source, options);
    }
    async installAndPersist(source, options) {
        await this.install(source, options);
        this.addSourceToSettings(source, options);
    }
    async remove(source, options) {
        await removeFromContext(this.context, source, options);
    }
    async removeAndPersist(source, options) {
        await this.remove(source, options);
        return this.removeSourceFromSettings(source, options);
    }
    async update(source) {
        await updateFromContext(this.context, source);
    }
    async checkForAvailableUpdates() {
        return checkForAvailableUpdatesFromContext(this.context);
    }
    parseSource(source) {
        return parsePackageSource(source);
    }
    getPackageIdentity(source, scope) {
        return getPackageIdentityFromContext(this.context, source, scope);
    }
    getGitInstallPath(source, scope) {
        return getGitInstallPathFromContext(this.context, source, scope);
    }
    async installParsedSource(parsed, scope) {
        await installParsedSourceFromContext(this.context, parsed, scope);
    }
    async updateGit(source, scope) {
        await updateGitFromContext(this.context, source, scope);
    }
    async gitHasAvailableUpdate(installedPath) {
        return gitHasAvailableUpdateFromContext(this.context, installedPath);
    }
    async refreshTemporaryGitSource(source, sourceStr) {
        await refreshTemporaryGitSourceFromContext(this.context, source, sourceStr);
    }
    async getLocalGitUpdateTarget(installedPath) {
        return getLocalGitUpdateTargetFromContext(this.context, installedPath);
    }
    getGlobalNpmRoot() {
        return getGlobalNpmRootFromContext(this.context);
    }
    async getLatestNpmVersion(packageSpec, range) {
        return getLatestNpmVersionFromContext(this.context, packageSpec, range);
    }
    spawnCommand(command, args, options) {
        return spawnProcess(command, args, {
            cwd: options?.cwd,
            stdio: isStdoutTakenOver() ? ["ignore", 2, 2] : "inherit",
            env: getCommandEnv(command),
        });
    }
    spawnCaptureCommand(command, args, options) {
        return spawnProcess(command, args, {
            cwd: options?.cwd,
            stdio: ["ignore", "pipe", "pipe"],
            env: getCommandEnv(command, options?.env),
        });
    }
    runCommandCapture(command, args, options) {
        return new Promise((resolvePromise, reject) => {
            const child = this.spawnCaptureCommand(command, args, options);
            let stdout = "";
            let stderr = "";
            let timedOut = false;
            const timeout = typeof options?.timeoutMs === "number"
                ? setTimeout(() => {
                    timedOut = true;
                    child.kill();
                }, options.timeoutMs)
                : undefined;
            child.stdout?.on("data", (data) => {
                stdout += data.toString();
            });
            child.stderr?.on("data", (data) => {
                stderr += data.toString();
            });
            child.once("error", (error) => {
                if (timeout)
                    clearTimeout(timeout);
                reject(error);
            });
            child.once("close", (code, signal) => {
                if (timeout)
                    clearTimeout(timeout);
                if (timedOut) {
                    reject(new Error(`${command} ${args.join(" ")} timed out after ${options?.timeoutMs}ms`));
                    return;
                }
                if (code === 0) {
                    resolvePromise(stdout.trim());
                    return;
                }
                const exitStatus = code === null ? `signal ${signal ?? "unknown"}` : `code ${code}`;
                reject(new Error(`${command} ${args.join(" ")} failed with ${exitStatus}: ${stderr || stdout}`));
            });
        });
    }
    runCommand(command, args, options) {
        return new Promise((resolvePromise, reject) => {
            const child = this.spawnCommand(command, args, options);
            child.on("error", reject);
            child.on("exit", (code) => {
                if (code === 0) {
                    resolvePromise();
                }
                else {
                    reject(new Error(`${command} ${args.join(" ")} failed with code ${code}`));
                }
            });
        });
    }
    runCommandSync(command, args) {
        const result = spawnProcessSync(command, args, {
            stdio: ["ignore", "pipe", "pipe"],
            encoding: "utf-8",
            env: getCommandEnv(command),
        });
        if (result.error || result.status !== 0) {
            throw new Error(`Failed to run ${command} ${args.join(" ")}: ${result.error?.message || result.stderr || result.stdout}`);
        }
        return (result.stdout || result.stderr || "").trim();
    }
}
//# sourceMappingURL=package-manager.js.map