"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.add = void 0;
const { Command } = require("commander");
const getConfig_js_1 = require("../lib/getConfig.js");
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const chalk = require("chalk");
const prompts = require("prompts");
const { execa } = require("execa");
const getPackageManager_js_1 = require("../lib/getPackageManager.js");
const spinner_js_1 = require("../lib/spinner.js");
const getRegistryComponent_js_1 = require("../lib/getRegistryComponent.js");
// Track installed components to prevent circular dependencies
const installedComponents = new Set();
exports.add = new Command()
    .name("add")
    .description("add components to your project")
    .argument("[components...]", "Components to add") // to add multiple components
    .option("-f, --force", "Force add components", false)
    .option("-c, --cwd", "Working directory. Defaults to the current working directory", process.cwd())
    .action((components, options) => __awaiter(void 0, void 0, void 0, function* () {
    const config = (0, getConfig_js_1.getConfig)(options.cwd);
    if (!config) {
        console.log(chalk.red("No configuration found. Please run initialize Vynk first to start adding components."));
        return;
    }
    // if no ts or tsx is configured, ask the user if they want to continue
    if (!config.tsx || !config.ts) {
        const { confirm } = yield prompts({
            type: "confirm",
            name: "confirm",
            message: "Only tsx or ts is currently supported. If you still want to install components without ts or tsx you have to configure them by yourself. Do you want to continue?",
            initial: false,
        });
        if (!confirm) {
            return;
        }
    }
    if (!components || components.length === 0) {
        console.log(chalk.red("No components provided"));
        return;
    }
    try {
        yield addComponents({
            components,
            config,
            cwd: options.cwd,
            options: {
                force: options.force,
            }
        });
    }
    catch (error) {
        console.error(chalk.red("Failed to add components:"), error);
        process.exit(1);
    }
}));
// Add Components to users project
const addComponents = (_a) => __awaiter(void 0, [_a], void 0, function* ({ components, config, cwd, options }) {
    const spinner = (0, spinner_js_1.getSpinner)("Adding components...");
    try {
        spinner.start();
        for (const component of components) {
            // Skip if already installed to prevent circular dependencies
            if (installedComponents.has(component)) {
                continue;
            }
            const registryComponent = yield (0, getRegistryComponent_js_1.getRegistryComponent)(component);
            if (!registryComponent) {
                spinner.stop();
                console.log(chalk.red(`Component ${component} not found in registry`));
                spinner.start();
                continue;
            }
            const componentType = registryComponent.type.split(":")[1];
            if (!componentType || !(componentType in config.paths)) {
                spinner.stop();
                console.log(chalk.red(`Invalid component type: ${registryComponent.type}`));
                spinner.start();
                continue;
            }
            // Create component directory in user's project (if it doesn't exist)
            const userComponentPath = path_1.default.resolve(cwd, config.paths[componentType]);
            // Create the default directory only if the "target" property is not defined for that comp
            if (!fs_1.default.existsSync(userComponentPath) && !registryComponent.files[0].target) {
                fs_1.default.mkdirSync(userComponentPath, { recursive: true });
            }
            // Install required dependencies first
            try {
                yield installDependencies(cwd, registryComponent.dependencies, registryComponent.devDependencies);
            }
            catch (error) {
                spinner.stop();
                console.log(chalk.red(`Failed to install dependencies for ${component}: ${error}`));
                spinner.start();
                continue;
            }
            // Copy files from my registry to user"s project
            for (const file of registryComponent.files) {
                try {
                    // If the target is defined use that, if it does not exists use the path from the config file
                    const targetDir = file.target
                        ? path_1.default.join(cwd, file.target)
                        : userComponentPath;
                    // Ensure target directory exists
                    if (!fs_1.default.existsSync(targetDir)) {
                        fs_1.default.mkdirSync(targetDir, { recursive: true });
                    }
                    const targetPath = path_1.default.join(targetDir, path_1.default.basename(file.path));
                    // Check if the component already exists and prompt user to overwrite
                    if (fs_1.default.existsSync(targetPath) && !options.force && !config.alwaysForce) {
                        spinner.stop();
                        const { confirm } = yield prompts({
                            type: "confirm",
                            name: "confirm",
                            message: `${component} already exists. Do you want to overwrite?`,
                            initial: false,
                        });
                        spinner.start();
                        if (!confirm) {
                            continue;
                        }
                    }
                    if (!file.content) {
                        throw new Error(`No content found for file: ${file.path}`);
                    }
                    fs_1.default.writeFileSync(targetPath, file.content);
                    // Mark component as installed
                    installedComponents.add(component);
                    // Recursively add all the dependent components
                    if (registryComponent.registryDependencies) {
                        yield addComponents({
                            components: registryComponent.registryDependencies,
                            config,
                            cwd,
                            options
                        });
                    }
                }
                catch (error) {
                    spinner.stop();
                    console.log(chalk.red(`Failed to add ${component} component`));
                    console.log(chalk.red(`Failed to copy file ${file.path}: ${error.message}`));
                    spinner.start();
                    continue;
                }
            }
        }
        spinner.succeed("Components added successfully");
    }
    catch (error) {
        spinner.fail("Failed to add components");
        throw error;
    }
    finally {
        spinner.stop();
    }
});
// Add package dependencies
const installDependencies = (cwd, dependencies, devDependencies) => __awaiter(void 0, void 0, void 0, function* () {
    if ((!dependencies || dependencies.length === 0) &&
        (!devDependencies || devDependencies.length === 0)) {
        return;
    }
    try {
        const packageManager = yield (0, getPackageManager_js_1.getPackageManager)(cwd);
        if (dependencies === null || dependencies === void 0 ? void 0 : dependencies.length) {
            yield execa(packageManager, [
                packageManager === "npm" ? "install" : "add",
                ...(packageManager === "deno"
                    ? dependencies.map((dep) => `npm:${dep}`)
                    : dependencies),
            ], {
                cwd,
            });
        }
        if (devDependencies === null || devDependencies === void 0 ? void 0 : devDependencies.length) {
            yield execa(packageManager, [
                packageManager === "npm" ? "install" : "add",
                "-D",
                ...(packageManager === "deno"
                    ? devDependencies.map((dep) => `npm:${dep}`)
                    : devDependencies),
            ], {
                cwd
            });
        }
    }
    catch (error) {
        throw new Error(`Failed to install packages: ${error}`);
    }
});
