import { execFile } from "node:child_process";
import { access, readFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { createLogger } from "../utils/logger.js";
import { BRIDGE_PACKAGE_ROOT, importBridgeSdkPackage } from "../utils/sdk-package-loader.js";
const execFileAsync = promisify(execFile);
const log = createLogger("tool-installer");
const bridgePackageRoot = BRIDGE_PACKAGE_ROOT;
const isWindows = process.platform === "win32";
function quoteCommandArg(value) {
    if (isWindows) {
        return `"${value.replaceAll('"', '\\"')}"`;
    }
    return `'${value.replaceAll("'", "'\\''")}'`;
}
function npmInstallIntoBridgeCommand(packages) {
    return `npm install --prefix ${quoteCommandArg(bridgePackageRoot)} ${packages.join(" ")}`;
}
function npmInstallIntoBridgeWithOptionsCommand(packages, options) {
    return `npm install --prefix ${quoteCommandArg(bridgePackageRoot)} ${packages.join(" ")} ${options.join(" ")}`;
}
function npmGlobalInstallCommand(packages) {
    return `npm install -g ${packages.join(" ")}`;
}
function npmUninstallFromBridgeCommand(packages) {
    return `npm uninstall --prefix ${quoteCommandArg(bridgePackageRoot)} ${packages.join(" ")}`;
}
function npmBridgeInstallCommand(packages, options = []) {
    return {
        kind: "execFile",
        command: npmExecutableName(),
        args: ["install", "--prefix", bridgePackageRoot, ...packages, ...options],
        display: options.length > 0
            ? npmInstallIntoBridgeWithOptionsCommand(packages, options)
            : npmInstallIntoBridgeCommand(packages),
    };
}
function npmExecutableName() {
    return isWindows ? "npm.cmd" : "npm";
}
/**
 * 构建 bridge 本地 npm 包安装/卸载命令。
 * 主流程：展示层保留可读命令；执行层使用参数数组，避免 Windows shell 引号被 npm 当作路径内容。
 */
function npmBridgeCommand(action, packages) {
    if (action === "install") {
        return npmBridgeInstallCommand(packages);
    }
    return {
        kind: "execFile",
        command: npmExecutableName(),
        args: [action, "--prefix", bridgePackageRoot, ...packages],
        display: npmUninstallFromBridgeCommand(packages),
    };
}
function npmGlobalCommand(action, packages) {
    return {
        kind: "execFile",
        command: npmExecutableName(),
        args: [action, "-g", ...packages],
        display: npmGlobalInstallCommand(packages),
    };
}
function shellToolCommand(command) {
    return { kind: "shell", display: command };
}
function nowIso() {
    return new Date().toISOString();
}
function finishAttempt(attempt, updates) {
    const finishedAt = nowIso();
    return {
        ...attempt,
        ...updates,
        finishedAt,
        durationMs: Date.parse(finishedAt) - Date.parse(attempt.startedAt),
    };
}
const TOOL_DEFINITIONS = {
    claude: {
        key: "claude",
        packageName: "@anthropic-ai/claude-agent-sdk",
        sdkPackageName: "@anthropic-ai/claude-agent-sdk",
        aliases: [],
        versionArgs: [],
        installCommands: [
            npmBridgeCommand("uninstall", ["@anthropic-ai/claude-agent-sdk", "@anthropic-ai/sdk"]),
            npmBridgeInstallCommand(["@anthropic-ai/sdk@latest"]),
            npmBridgeInstallCommand(["@anthropic-ai/claude-agent-sdk@latest"]),
            npmBridgeInstallCommand(["@anthropic-ai/claude-agent-sdk@latest"], ["--legacy-peer-deps"]),
            npmGlobalCommand("install", ["@anthropic-ai/claude-code@latest"]),
        ],
        uninstallCommands: [
            npmBridgeCommand("uninstall", ["@anthropic-ai/claude-agent-sdk"]),
        ],
        cliDependency: {
            packageName: "@anthropic-ai/claude-code",
            aliases: isWindows
                ? ["claude.cmd", "claude.exe", "claude"]
                : ["claude"],
            versionArgs: ["--version"],
            installCommands: [npmGlobalCommand("install", ["@anthropic-ai/claude-code@latest"])],
        },
    },
    claudecode: {
        key: "claudecode",
        packageName: "@anthropic-ai/claude-agent-sdk",
        sdkPackageName: "@anthropic-ai/claude-agent-sdk",
        aliases: [],
        versionArgs: [],
        installCommands: [
            npmBridgeCommand("uninstall", ["@anthropic-ai/claude-agent-sdk", "@anthropic-ai/sdk"]),
            npmBridgeInstallCommand(["@anthropic-ai/sdk@latest"]),
            npmBridgeInstallCommand(["@anthropic-ai/claude-agent-sdk@latest"]),
            npmBridgeInstallCommand(["@anthropic-ai/claude-agent-sdk@latest"], ["--legacy-peer-deps"]),
            npmGlobalCommand("install", ["@anthropic-ai/claude-code@latest"]),
        ],
        uninstallCommands: [
            npmBridgeCommand("uninstall", ["@anthropic-ai/claude-agent-sdk"]),
        ],
        cliDependency: {
            packageName: "@anthropic-ai/claude-code",
            aliases: isWindows
                ? ["claude.cmd", "claude.exe", "claude"]
                : ["claude"],
            versionArgs: ["--version"],
            installCommands: [npmGlobalCommand("install", ["@anthropic-ai/claude-code@latest"])],
        },
    },
    opencode: {
        key: "opencode",
        packageName: "@opencode-ai/sdk",
        sdkPackageName: "@opencode-ai/sdk",
        aliases: isWindows
            ? ["opencode.cmd", "opencode.exe", "opencode"]
            : ["opencode"],
        versionArgs: ["--version"],
        installCommands: [
            npmBridgeCommand("install", ["@opencode-ai/sdk@latest", "opencode-ai@latest"]),
        ],
        uninstallCommands: isWindows
            ? [
                npmBridgeCommand("uninstall", ["@opencode-ai/sdk", "opencode-ai"]),
            ]
            : [
                npmBridgeCommand("uninstall", ["@opencode-ai/sdk", "opencode-ai"]),
                shellToolCommand("opencode uninstall --force"),
                shellToolCommand("rm -f ~/.opencode/bin/opencode"),
            ],
        extraSearchPaths: () => {
            const home = os.homedir();
            return [
                process.env.OPENCODE_INSTALL_DIR || "",
                process.env.XDG_BIN_DIR || "",
                path.join(home, "bin"),
                path.join(home, ".opencode", "bin"),
                path.join(home, "go", "bin"),
            ].filter(Boolean);
        },
    },
    codex: {
        key: "codex",
        packageName: "@openai/codex-sdk",
        sdkPackageName: "@openai/codex-sdk",
        aliases: [],
        versionArgs: [],
        installCommands: [npmBridgeCommand("install", ["@openai/codex-sdk@latest"])],
        uninstallCommands: [npmBridgeCommand("uninstall", ["@openai/codex-sdk"])],
    },
};
export const SUPPORTED_INSTALLABLE_TOOLS = Object.freeze(Object.keys(TOOL_DEFINITIONS));
export const SUPPORTED_UNINSTALLABLE_TOOLS = Object.freeze(Object.keys(TOOL_DEFINITIONS).filter((tool) => TOOL_DEFINITIONS[tool].uninstallCommands.length > 0));
/**
 * 返回工具对应的全局卸载命令副本，用于诊断与测试卸载覆盖范围。
 */
export function getToolUninstallCommands(tool) {
    const normalizedTool = String(tool || "")
        .trim()
        .toLowerCase();
    return (TOOL_DEFINITIONS[normalizedTool]?.uninstallCommands || []).map((command) => command.display);
}
/**
 * 返回工具对应的安装/卸载执行参数副本，用于验证 Windows 下不会把路径引号传给 npm。
 */
export function getToolCommandSpecs(tool, phase) {
    const normalizedTool = String(tool || "")
        .trim()
        .toLowerCase();
    const commands = phase === "install"
        ? TOOL_DEFINITIONS[normalizedTool]?.installCommands
        : TOOL_DEFINITIONS[normalizedTool]?.uninstallCommands;
    return (commands || []).map((command) => ({
        command: command.kind === "execFile" ? command.command : command.display,
        args: command.kind === "execFile" ? [...command.args] : [],
        display: command.display,
        kind: command.kind,
    }));
}
function parseVersion(output) {
    const normalized = String(output || "").trim();
    if (!normalized) {
        return null;
    }
    const match = normalized.match(/(\d+\.\d+\.\d+(?:[-+][\w.-]+)?)/);
    return match?.[1] || normalized.split(/\s+/)[0] || null;
}
function splitPathEntries(rawPath) {
    return String(rawPath || "")
        .split(path.delimiter)
        .map((item) => item.trim())
        .filter(Boolean);
}
function uniquePaths(paths) {
    return [...new Set(paths.filter(Boolean))];
}
function describeExecError(error) {
    if (!error || typeof error !== "object") {
        return "unknown detection error";
    }
    const code = "code" in error ? String(error.code || "").trim() : "";
    const errno = "errno" in error ? String(error.errno || "").trim() : "";
    const message = "message" in error ? String(error.message || "").trim() : "";
    if (code === "ENOENT") {
        return "command not found in PATH";
    }
    if (code === "EACCES" || code === "EPERM") {
        return "command found but not executable";
    }
    if (code === "ETIMEDOUT") {
        return "version command timed out";
    }
    if (code === "UNKNOWN" && /not recognized|not found/i.test(message)) {
        return "command not found in PATH";
    }
    if (errno === "-4058") {
        return "command not found in PATH";
    }
    if (errno === "-4048" || errno === "-13") {
        return "command found but not executable";
    }
    return message || "command execution failed";
}
export function isVoltaShimPath(commandPath) {
    const normalizedPath = path
        .normalize(commandPath)
        .replaceAll("\\", "/")
        .toLowerCase();
    return /(?:^|\/)\.?volta\/bin\//.test(normalizedPath);
}
export function buildSdkCliInstallStatus(tool, sdkResult, cliResult, requiresCli) {
    const isSdkInstalled = Boolean(sdkResult.executable);
    const isCliInstalled = requiresCli ? Boolean(cliResult.executable) : true;
    const errorMessages = [
        isSdkInstalled ? null : sdkResult.error,
        isCliInstalled ? null : cliResult.error,
    ].filter(Boolean);
    return {
        tool,
        installed: isSdkInstalled && isCliInstalled,
        executable: [sdkResult.executable, cliResult.executable]
            .filter(Boolean)
            .join("; ") || null,
        version: sdkResult.version,
        installing: false,
        error: errorMessages.length > 0 ? errorMessages.join("; ") : null,
    };
}
async function isVoltaPackageInstalled(packageName) {
    try {
        const { stdout, stderr } = await execFileAsync("volta", ["list", "--format", "plain"], {
            timeout: 20_000,
        });
        const output = `${stdout || ""}\n${stderr || ""}`;
        const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
        return new RegExp(`^package\\s+${escapedPackageName}(?:@|\\s)`, "im").test(output);
    }
    catch {
        return false;
    }
}
/**
 * 在 Windows 上执行命令（处理 .cmd/.bat 文件）
 * 在 Unix 系统上直接执行
 */
async function executeVersionCommand(command, args) {
    if (isWindows) {
        // Windows 上需要通过 cmd.exe 执行命令（尤其是 .cmd/.bat 文件）
        const result = await execFileAsync("cmd.exe", ["/d", "/s", "/c", `${command} ${args.join(" ")}`], { timeout: 20_000 });
        return { stdout: result.stdout || "", stderr: result.stderr || "" };
    }
    const result = await execFileAsync(command, args, { timeout: 20_000 });
    return { stdout: result.stdout || "", stderr: result.stderr || "" };
}
async function resolveExecutableCandidate(definition) {
    const searchPaths = uniquePaths([
        ...splitPathEntries(process.env.PATH),
        ...(definition.extraSearchPaths ? definition.extraSearchPaths() : []),
        // Windows 上额外搜索 Volta 等常见路径
        ...(isWindows
            ? [
                path.join(process.env.APPDATA || "", "npm"),
                path.join(process.env.LOCALAPPDATA || "", "Volta", "bin"),
                path.join(process.env.APPDATA || "", "Volta", "bin"),
                "C:\\Program Files\\nodejs",
            ]
            : [
                path.join(os.homedir(), ".local", "bin"),
                "/usr/local/bin",
                "/usr/bin",
            ]),
    ]);
    let foundCommandPath = null;
    let executionFailureMessage = null;
    for (const alias of definition.aliases) {
        try {
            const { stdout, stderr } = await executeVersionCommand(alias, definition.versionArgs);
            const version = parseVersion(`${stdout || ""}\n${stderr || ""}`);
            if (!version) {
                return {
                    executable: alias,
                    version: null,
                    error: "command executable but version output unavailable",
                };
            }
            return { executable: alias, version, error: null };
        }
        catch (error) {
            executionFailureMessage = describeExecError(error);
            if (!isWindows && executionFailureMessage !== "command not found in PATH") {
                return {
                    executable: alias,
                    version: null,
                    error: executionFailureMessage,
                };
            }
        }
        for (const dir of searchPaths) {
            const absolutePath = path.join(dir, alias);
            try {
                await access(absolutePath);
                foundCommandPath = absolutePath;
                if (isVoltaShimPath(absolutePath) &&
                    !(await isVoltaPackageInstalled(definition.packageName))) {
                    foundCommandPath = null;
                    continue;
                }
                const { stdout, stderr } = await executeVersionCommand(absolutePath, definition.versionArgs);
                const version = parseVersion(`${stdout || ""}\n${stderr || ""}`);
                if (!version) {
                    return {
                        executable: absolutePath,
                        version: null,
                        error: "command executable but version output unavailable",
                    };
                }
                return { executable: absolutePath, version, error: null };
            }
            catch (error) {
                executionFailureMessage = describeExecError(error);
                if (foundCommandPath) {
                    return {
                        executable: foundCommandPath,
                        version: null,
                        error: executionFailureMessage,
                    };
                }
            }
        }
    }
    if (foundCommandPath) {
        return {
            executable: foundCommandPath,
            version: null,
            error: executionFailureMessage || "command found but failed to execute",
        };
    }
    return { executable: null, version: null, error: "command not installed" };
}
async function readPackageVersion(packageJsonPath) {
    try {
        const rawPackageJson = await readFile(packageJsonPath, "utf8");
        const parsedPackageJson = JSON.parse(rawPackageJson);
        return typeof parsedPackageJson.version === "string"
            ? parsedPackageJson.version
            : null;
    }
    catch {
        return null;
    }
}
async function readInstalledPackageVersion(packageName) {
    const packageJsonPath = path.join(bridgePackageRoot, "node_modules", ...packageName.split("/"), "package.json");
    return readPackageVersion(packageJsonPath);
}
/**
 * 检测 SDK provider 包是否能被当前 bridge 进程解析。
 * 主流程：使用与运行时代码相同的模块解析上下文，避免把全局 CLI 安装误报为 SDK 可用。
 */
async function resolveSdkPackageCandidate(definition) {
    const sdkPackageName = definition.sdkPackageName;
    if (!sdkPackageName) {
        return resolveExecutableCandidate(definition);
    }
    try {
        await importBridgeSdkPackage(sdkPackageName);
        return {
            executable: sdkPackageName,
            version: await readInstalledPackageVersion(sdkPackageName),
            error: null,
        };
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error || "");
        return {
            executable: null,
            version: null,
            error: message.includes(sdkPackageName)
                ? `SDK package ${sdkPackageName} is not installed in aws-runtime-bridge`
                : message || `SDK package ${sdkPackageName} is not installed in aws-runtime-bridge`,
        };
    }
}
/**
 * 检测工具必需的终端 CLI 是否可执行。
 * 主流程：SDK 负责程序化调用，CLI 负责实际 Claude Code 运行；两者都可用才算集成可用。
 */
async function resolveCliDependencyCandidate(definition) {
    const cliDependency = definition.cliDependency;
    if (!cliDependency) {
        return { executable: null, version: null, error: null };
    }
    const cliResult = await resolveExecutableCandidate(cliDependency);
    if (cliResult.executable) {
        return cliResult;
    }
    return {
        ...cliResult,
        error: `CLI package ${cliDependency.packageName} is not installed or command is unavailable: ${cliResult.error || "command not installed"}`,
    };
}
function describeCommandFailure(error) {
    if (!error || typeof error !== "object") {
        return {
            step: "install_command",
            command: null,
            message: String(error || "command failed"),
            code: null,
            signal: null,
            stdout: "",
            stderr: "",
        };
    }
    const failure = error;
    return {
        step: "install_command",
        command: null,
        message: typeof failure.message === "string" ? failure.message : String(error),
        code: typeof failure.code === "string" || typeof failure.code === "number" ? failure.code : null,
        signal: typeof failure.signal === "string" ? failure.signal : null,
        stdout: typeof failure.stdout === "string" ? failure.stdout : "",
        stderr: typeof failure.stderr === "string" ? failure.stderr : "",
    };
}
function buildFailureFromAttempt(attempt, fallbackMessage) {
    return {
        step: attempt.step,
        command: attempt.command,
        message: attempt.error || fallbackMessage,
        stdout: attempt.stdout || "",
        stderr: attempt.stderr || "",
        code: attempt.code ?? null,
        signal: attempt.signal ?? null,
    };
}
async function runToolCommand(command) {
    if (command.kind === "execFile") {
        const needsWindowsCmdShim = isWindows && /\.(?:cmd|bat)$/i.test(command.command);
        const result = needsWindowsCmdShim
            ? await execFileAsync("cmd.exe", ["/d", "/s", "/c", command.command, ...command.args], {
                timeout: 10 * 60 * 1000,
                windowsVerbatimArguments: false,
            })
            : await execFileAsync(command.command, command.args, {
                timeout: 10 * 60 * 1000,
                windowsVerbatimArguments: false,
            });
        return { stdout: result.stdout || "", stderr: result.stderr || "" };
    }
    if (isWindows) {
        const result = await execFileAsync("cmd.exe", ["/d", "/s", "/c", command.display], {
            timeout: 10 * 60 * 1000,
        });
        return { stdout: result.stdout || "", stderr: result.stderr || "" };
    }
    const result = await execFileAsync("/bin/sh", ["-lc", command.display], {
        timeout: 10 * 60 * 1000,
    });
    return { stdout: result.stdout || "", stderr: result.stderr || "" };
}
/**
 * 检查单个工具的 CLI 可执行状态，供实例状态展示与初始化前判断使用。
 */
export async function detectToolInstallStatus(tool) {
    const normalizedTool = String(tool || "")
        .trim()
        .toLowerCase();
    const definition = TOOL_DEFINITIONS[normalizedTool];
    if (!definition) {
        log.warn("Unsupported tool status requested", { tool: normalizedTool });
        return {
            tool: normalizedTool,
            installed: false,
            executable: null,
            version: null,
            installing: false,
            error: "unsupported tool",
        };
    }
    const sdkResult = await resolveSdkPackageCandidate(definition);
    if (definition.sdkPackageName) {
        const cliResult = await resolveCliDependencyCandidate(definition);
        return buildSdkCliInstallStatus(normalizedTool, sdkResult, cliResult, Boolean(definition.cliDependency));
    }
    const result = await resolveExecutableCandidate(definition);
    return {
        tool: normalizedTool,
        installed: Boolean(result.executable),
        executable: result.executable,
        version: result.version,
        installing: false,
        error: result.error,
    };
}
/**
 * 批量检查工具安装状态，统一返回给前端面板和实例状态持久化。
 */
export async function detectToolStatuses(tools) {
    const normalizedTools = [
        ...new Set((Array.isArray(tools) ? tools : [])
            .map((item) => String(item || "")
            .trim()
            .toLowerCase())
            .filter(Boolean)),
    ];
    const entries = await Promise.all(normalizedTools.map(async (tool) => [tool, await detectToolInstallStatus(tool)]));
    return Object.fromEntries(entries);
}
/**
 * 根据勾选工具自动安装缺失 CLI，安装后重新检测状态并返回。
 */
export async function ensureToolsInstalled(tools) {
    log.info("Install request received", {
        requestedTools: tools,
        bridgePackageRoot,
        platform: process.platform,
        node: process.version,
    });
    const initialStatuses = await detectToolStatuses(tools);
    log.info("Initial install status detected", { statuses: initialStatuses });
    const nextStatuses = { ...initialStatuses };
    for (const tool of Object.keys(initialStatuses)) {
        const current = initialStatuses[tool];
        if (current.installed) {
            log.info("Skipping install because tool SDK is already available", {
                tool,
                version: current.version,
                executable: current.executable,
            });
            continue;
        }
        const definition = TOOL_DEFINITIONS[tool];
        if (!definition) {
            log.warn("Skipping install because tool is unsupported", { tool, current });
            continue;
        }
        const attempts = [];
        const initialAttempt = finishAttempt({
            step: "detect_initial",
            label: "检测当前安装状态",
            command: null,
            status: "running",
            startedAt: nowIso(),
        }, {
            status: current.installed ? "success" : "error",
            error: current.installed ? null : current.error,
        });
        attempts.push(initialAttempt);
        let lastError = current.error;
        let lastFailure = current.installed
            ? null
            : buildFailureFromAttempt(initialAttempt, current.error || "tool is not installed");
        for (const [index, command] of definition.installCommands.entries()) {
            const commandAttempt = {
                step: `install_command_${index + 1}`,
                label: `执行安装命令 ${index + 1}/${definition.installCommands.length}`,
                command: command.display,
                status: "running",
                startedAt: nowIso(),
            };
            try {
                log.info("Running install command", {
                    tool,
                    command: command.display,
                    sdkPackageName: definition.sdkPackageName || null,
                    packageName: definition.packageName,
                });
                const commandResult = await runToolCommand(command);
                attempts.push(finishAttempt(commandAttempt, {
                    status: "success",
                    stdout: commandResult.stdout,
                    stderr: commandResult.stderr,
                    error: null,
                }));
                log.info("Install command completed", {
                    tool,
                    command: command.display,
                    stdout: commandResult.stdout,
                    stderr: commandResult.stderr,
                });
                const detected = await detectToolInstallStatus(tool);
                const detectAttempt = finishAttempt({
                    step: `detect_after_command_${index + 1}`,
                    label: `验证安装结果 ${index + 1}/${definition.installCommands.length}`,
                    command: null,
                    status: "running",
                    startedAt: nowIso(),
                }, {
                    status: detected.installed ? "success" : "error",
                    error: detected.installed ? null : detected.error,
                });
                attempts.push(detectAttempt);
                nextStatuses[tool] = {
                    ...detected,
                    currentStep: detected.installed ? "安装完成" : detectAttempt.label,
                    attempts: [...attempts],
                    failure: detected.installed
                        ? null
                        : buildFailureFromAttempt(detectAttempt, detected.error || "install verification failed"),
                };
                log.info("Post-install status detected", { tool, status: detected });
                if (detected.installed) {
                    lastError = null;
                    lastFailure = null;
                    break;
                }
                lastError = detected.error;
                lastFailure = buildFailureFromAttempt(detectAttempt, detected.error || "install verification failed");
            }
            catch (error) {
                const failure = {
                    ...describeCommandFailure(error),
                    step: commandAttempt.step,
                    command: command.display,
                };
                attempts.push(finishAttempt(commandAttempt, {
                    status: "error",
                    error: failure.message,
                    stdout: failure.stdout,
                    stderr: failure.stderr,
                    code: failure.code,
                    signal: failure.signal,
                }));
                log.error("Install command failed", {
                    tool,
                    command: command.display,
                    failure,
                });
                lastError = failure.message;
                lastFailure = failure;
            }
        }
        if (!nextStatuses[tool]?.installed) {
            nextStatuses[tool] = {
                ...nextStatuses[tool],
                tool,
                installed: false,
                installing: false,
                error: lastError || "install failed",
                currentStep: attempts.at(-1)?.label || "安装失败",
                attempts: [...attempts],
                failure: lastFailure,
            };
            log.warn("Tool SDK remains unavailable after install attempts", {
                tool,
                status: nextStatuses[tool],
                attemptedCommands: definition.installCommands,
                attemptedCommandDisplays: definition.installCommands.map((command) => command.display),
            });
        }
    }
    log.info("Install request completed", { statuses: nextStatuses });
    return nextStatuses;
}
/**
 * 按工具定义执行全局卸载命令，随后重新检测并返回最新安装状态。
 */
export async function uninstallTools(tools) {
    const normalizedTools = [
        ...new Set((Array.isArray(tools) ? tools : [])
            .map((item) => String(item || "")
            .trim()
            .toLowerCase())
            .filter(Boolean)),
    ];
    const nextStatuses = {};
    for (const tool of normalizedTools) {
        const definition = TOOL_DEFINITIONS[tool];
        if (!definition) {
            nextStatuses[tool] = await detectToolInstallStatus(tool);
            continue;
        }
        let lastError = null;
        for (const command of definition.uninstallCommands) {
            try {
                await runToolCommand(command);
                lastError = null;
                const detectedAfterCommand = await detectToolInstallStatus(tool);
                if (!detectedAfterCommand.installed) {
                    nextStatuses[tool] = detectedAfterCommand;
                    break;
                }
            }
            catch (error) {
                lastError =
                    error instanceof Error
                        ? error.message
                        : String(error || "uninstall failed");
            }
        }
        const detected = await detectToolInstallStatus(tool);
        nextStatuses[tool] = {
            ...detected,
            error: detected.installed
                ? lastError || detected.error || "uninstall completed but command is still available"
                : detected.error,
        };
    }
    return nextStatuses;
}
