#!/usr/bin/env node
"use strict";
/**
 * install subcommand for dominds CLI (Apps)
 *
 * Usage:
 *   dominds install <spec|path> [--local] [--id <appId>] [--enable] [--force]
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const app_lock_file_1 = require("../apps/app-lock-file");
const configuration_file_1 = require("../apps/configuration-file");
const local_package_root_1 = require("../apps/local-package-root");
const manifest_1 = require("../apps/manifest");
const resolution_file_1 = require("../apps/resolution-file");
const run_app_json_1 = require("../apps/run-app-json");
const workspace_app_state_1 = require("../apps/workspace-app-state");
const rtws_1 = require("../rtws");
const apps_cli_hints_1 = require("./apps-cli-hints");
function printHelp() {
    console.log(`Usage:
  dominds install <spec|path> [--local] [--id <appId>] [--enable] [--force]

Options:
  --local              Treat <spec|path> as a local package directory (dev package)
  --id <appId>         Require app id (must match app --dominds-app appId)
  --enable             Remove the app from disabledApps after install
  --force              Reserved for future use; currently ignored

Notes:
  - install adds the app to .minds/app.yaml dependencies.
  - app resolution source is determined dynamically via .apps/configuration.yaml resolutionStrategy.
  - install may also refresh .minds/app-lock.yaml, .apps/configuration.yaml, and
    .apps/resolution.yaml for the installed app, but it is not a health check.
  - ${(0, apps_cli_hints_1.formatMutationBoundaryNote)({
        commandName: 'install',
        layerDescription: '.minds/app.yaml plus derived lock/configuration/resolution state for the installed app',
    })}
`);
}
function parseArgs(argv) {
    const positional = [];
    let force = false;
    let enable = false;
    let local = false;
    let idOverride = null;
    for (let i = 0; i < argv.length; i += 1) {
        const arg = argv[i];
        if (arg === '--help' || arg === '-h') {
            printHelp();
            process.exit(0);
        }
        if (arg === '--force') {
            force = true;
            continue;
        }
        if (arg === '--enable') {
            enable = true;
            continue;
        }
        if (arg === '--local') {
            local = true;
            continue;
        }
        if (arg === '--id') {
            const value = argv[i + 1];
            if (!value)
                throw new Error('Missing value for --id');
            idOverride = value.trim() || null;
            i += 1;
            continue;
        }
        if (arg.startsWith('-'))
            throw new Error(`Unknown option: ${arg}`);
        positional.push(arg);
    }
    if (positional.length !== 1)
        throw new Error('install requires exactly one <spec|path>');
    return { specOrPath: positional[0], force, enable, local, idOverride };
}
async function pathIsDirectory(pathAbs) {
    try {
        const stat = await promises_1.default.stat(pathAbs);
        return stat.isDirectory();
    }
    catch {
        return false;
    }
}
async function resolveInstallSource(params) {
    if (params.treatAsExplicitLocalPath) {
        return { kind: 'local', packageRootAbs: node_path_1.default.resolve(params.rtwsRootAbs, params.specOrPath) };
    }
    const loadedConfig = await (0, configuration_file_1.loadAppsConfigurationFile)({ rtwsRootAbs: params.rtwsRootAbs });
    if (loadedConfig.kind === 'error') {
        throw new Error(`failed to read .apps/configuration.yaml: ${loadedConfig.errorText}`);
    }
    const strategy = (0, configuration_file_1.normalizeAppsResolutionStrategy)(loadedConfig.file.resolutionStrategy);
    for (const item of strategy.order) {
        if (item === 'local') {
            const packageRootAbs = await (0, local_package_root_1.resolveLocalAppPackageRootAbs)({
                rtwsRootAbs: params.rtwsRootAbs,
                appId: params.specOrPath,
                localRoots: strategy.localRoots,
                previousResolutionEntry: null,
            });
            if (packageRootAbs !== null)
                return { kind: 'local', packageRootAbs };
            continue;
        }
        if (item === 'npx')
            return { kind: 'npx', spec: params.specOrPath };
        const exhaustive = item;
        throw new Error(`Unreachable install resolution strategy item: ${String(exhaustive)}`);
    }
    return { kind: 'npx', spec: params.specOrPath };
}
async function main(argv = process.argv.slice(2)) {
    const rtwsRootAbs = (0, rtws_1.domindsRtwsRootAbs)();
    let args;
    try {
        args = parseArgs(argv);
    }
    catch (err) {
        console.error(err instanceof Error ? err.message : String(err));
        printHelp();
        process.exit(1);
        return;
    }
    const specOrPath = args.specOrPath.trim();
    if (specOrPath === '') {
        console.error('Error: <spec|path> must be non-empty');
        process.exit(1);
        return;
    }
    const localAbs = node_path_1.default.resolve(rtwsRootAbs, specOrPath);
    const installSource = await resolveInstallSource({
        rtwsRootAbs,
        specOrPath,
        treatAsExplicitLocalPath: args.local || (await pathIsDirectory(localAbs)),
    });
    const installJson = installSource.kind === 'local'
        ? await (0, run_app_json_1.runDomindsAppJsonViaLocalPackage)({ packageRootAbs: installSource.packageRootAbs })
        : await (0, run_app_json_1.runDomindsAppJsonViaNpx)({ spec: installSource.spec, cwdAbs: rtwsRootAbs });
    if (args.idOverride && args.idOverride !== installJson.appId) {
        console.error(`Error: --id '${args.idOverride}' does not match appId '${installJson.appId}'`);
        console.error((0, apps_cli_hints_1.formatDoctorGuidance)({ appId: installJson.appId }));
        process.exit(1);
        return;
    }
    const loadedManifest = await (0, manifest_1.loadDomindsAppManifest)({
        packageRootAbs: rtwsRootAbs,
        manifestRelPath: manifest_1.DEFAULT_DOMINDS_APP_MANIFEST_REL_PATH,
    });
    const manifest = loadedManifest.kind === 'ok' ? loadedManifest.manifest : (0, manifest_1.makeDefaultRtwsAppManifest)();
    const nextManifest = (0, manifest_1.upsertManifestDependency)({
        manifest,
        dependency: { id: installJson.appId },
    });
    await (0, manifest_1.writeDomindsAppManifestIfChanged)({
        packageRootAbs: rtwsRootAbs,
        manifestRelPath: manifest_1.DEFAULT_DOMINDS_APP_MANIFEST_REL_PATH,
        manifest: nextManifest,
    });
    const loadedConfig = await (0, configuration_file_1.loadAppsConfigurationFile)({ rtwsRootAbs });
    if (loadedConfig.kind === 'error') {
        console.error(`Error: failed to read .apps/configuration.yaml: ${loadedConfig.errorText}`);
        console.error((0, apps_cli_hints_1.formatDoctorGuidance)({ appId: installJson.appId }));
        process.exit(1);
        return;
    }
    const nextConfig = (0, configuration_file_1.setAppDisabledInConfiguration)({
        existing: loadedConfig.file,
        appId: installJson.appId,
        disabled: args.enable
            ? false
            : isAppCurrentlyDisabled(loadedConfig.file.disabledApps, installJson.appId),
    });
    await (0, configuration_file_1.writeAppsConfigurationFileIfChanged)({ rtwsRootAbs, file: nextConfig });
    const loadedLock = await (0, app_lock_file_1.loadAppLockFile)({ rtwsRootAbs });
    if (loadedLock.kind === 'error') {
        console.error(`Error: failed to read .minds/app-lock.yaml: ${loadedLock.errorText}`);
        console.error((0, apps_cli_hints_1.formatDoctorGuidance)({ appId: installJson.appId }));
        process.exit(1);
        return;
    }
    const nextLock = (0, app_lock_file_1.upsertLockedApp)({
        existing: loadedLock.file,
        next: {
            id: installJson.appId,
            package: {
                name: installJson.package.name,
                version: installJson.package.version,
            },
        },
    });
    await (0, app_lock_file_1.writeAppLockFileIfChanged)({ rtwsRootAbs, file: nextLock });
    if (installSource.kind === 'local') {
        const loadedResolution = await (0, resolution_file_1.loadAppsResolutionFile)({ rtwsRootAbs });
        if (loadedResolution.kind === 'error') {
            console.error(`Error: failed to read .apps/resolution.yaml: ${loadedResolution.errorText}`);
            console.error((0, apps_cli_hints_1.formatDoctorGuidance)({ appId: installJson.appId }));
            process.exit(1);
            return;
        }
        const nextResolution = (0, resolution_file_1.upsertResolvedApp)({
            existing: loadedResolution.file,
            next: {
                id: installJson.appId,
                enabled: true,
                source: { kind: 'local', pathAbs: installSource.packageRootAbs },
                assignedPort: null,
                installJson,
            },
        });
        await (0, resolution_file_1.writeAppsResolutionFileIfChanged)({ rtwsRootAbs, file: nextResolution });
    }
    await (0, workspace_app_state_1.refreshAppsDerivedState)({ rtwsRootAbs });
    void args.force;
    console.log(installSource.kind === 'local'
        ? `Installed app '${installJson.appId}' from local package: ${installSource.packageRootAbs}`
        : `Installed app '${installJson.appId}' via resolver strategy seed: ${specOrPath}`);
    console.log((0, apps_cli_hints_1.formatMutationBoundaryNote)({
        commandName: 'install',
        layerDescription: '.minds/app.yaml plus derived lock/configuration/resolution state for the installed app',
        appId: installJson.appId,
    }));
    if (args.enable) {
        console.log(`Enabled app '${installJson.appId}' in .apps/configuration.yaml and refreshed derived state.`);
    }
}
function isAppCurrentlyDisabled(disabledApps, appId) {
    return (disabledApps ?? []).includes(appId);
}
if (require.main === module) {
    main().catch((err) => {
        console.error('Unhandled error:', err);
        process.exit(1);
    });
}
