import fs from "node:fs/promises";
import path from "node:path";
import z from "zod";
import { getErrorMeta, getLogger } from "../../../util/logger.js";
import { createToolFactory, ToolCategory } from "../tools2/types.js";
async function readPackageJson(appRootDirPath) {
    try {
        const packageJsonPath = path.join(appRootDirPath, "package.json");
        const content = await fs.readFile(packageJsonPath, "utf-8");
        return JSON.parse(content);
    }
    catch {
        return null;
    }
}
function detectVersionChanges(packages, currentPackageJson) {
    if (!currentPackageJson)
        return [];
    const changes = [];
    for (const pkg of packages) {
        const isDev = pkg.dev ?? false;
        const currentVersion = isDev
            ? currentPackageJson.devDependencies?.[pkg.name]
            : currentPackageJson.dependencies?.[pkg.name];
        if (currentVersion && pkg.version && currentVersion !== pkg.version) {
            changes.push({
                name: pkg.name,
                oldVersion: currentVersion,
                newVersion: pkg.version,
                isDev,
            });
        }
    }
    return changes;
}
export const buildInstallPackagesToolFactory = createToolFactory("build_installPackages", ({ clark, services, }) => {
    return {
        category: ToolCategory.GENERAL,
        description: `
      Install packages using the project's package manager.

      (Recommended) Provide a list of packages to install with optional version and dev flag:
      - Example: packages: [{name: 'react', version: '18.2.0'}, {name: '@types/react', dev: true}]

      If no packages are specified, runs a standard install to update dependencies from package.json.

      This tool checks for existing package versions, warns about version changes as version upgrades may break existing components.
    `,
        inputSchema: z.object({
            packages: z
                .array(z.object({
                name: z
                    .string()
                    .describe("Package name (e.g., 'react', '@types/node', 'lodash')"),
                version: z
                    .string()
                    .optional()
                    .describe("Version specifier (e.g., '18.2.0', '^18.0.0', 'latest'). Defaults to 'latest' if not specified"),
                dev: z
                    .boolean()
                    .optional()
                    .describe("Install as dev dependency (--save-dev). Defaults to false"),
            }))
                .optional()
                .describe("Packages to install. If not provided or empty, runs a standard install to update dependencies from package.json"),
        }),
        execute: async ({ packages }) => {
            try {
                // create draft marker for package.json
                await services.draftInterface.createDraftMarkerOnly("package.json");
                let output;
                let versionChanges = [];
                if (packages && packages.length > 0) {
                    const currentPackageJson = await readPackageJson(services.appShell.appRootDirPath);
                    versionChanges = detectVersionChanges(packages, currentPackageJson);
                    // Mode 1: Install specific packages
                    output = await services.appShell.installSpecificPackages(packages, clark.context.abortController?.signal);
                    const packageNames = packages.map((pkg) => pkg.name).join(", ");
                    let message = `Successfully installed packages: ${packageNames}`;
                    // Add version change information if any
                    if (versionChanges.length > 0) {
                        const changeDetails = versionChanges
                            .map((change) => `  - ${change.name}: ${change.oldVersion} → ${change.newVersion}${change.isDev ? " (dev)" : ""}`)
                            .join("\n");
                        message += `\n\nVersion changes detected:\n${changeDetails}\n\n⚠️  WARNING: Version changes may affect existing components that depend on these packages. Consider checking for breaking changes.`;
                    }
                    return {
                        output,
                        message,
                        versionChanges,
                    };
                }
                else {
                    // Mode 2: Install from package.json
                    output = await services.appShell.runPackageManagerInstall(clark.context.abortController?.signal);
                    return {
                        output,
                        message: `Successfully ran package manager install command`,
                    };
                }
            }
            catch (error) {
                getLogger().error("Error installing packages", getErrorMeta(error));
                throw error;
            }
        },
        getSummary: (input, output) => {
            if (input.packages && input.packages.length > 0) {
                const names = input.packages.map((p) => p.name).join(", ");
                const versionWarning = output.versionChanges?.length
                    ? ` (${output.versionChanges.length} version change${output.versionChanges.length === 1 ? "" : "s"})`
                    : "";
                return {
                    input: `${input.packages.length} package${input.packages.length === 1 ? "" : "s"}: ${names}`,
                    output: `Installed${versionWarning}`,
                };
            }
            return {
                output: "Dependencies updated",
            };
        },
    };
});
//# sourceMappingURL=build-install-packages.js.map