// tools/registry/install.ts — AC-6: Install a spec from the registry to local project
import { readdir, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { compactResult } from '../output-formatter.js';
import { generateSpecId, slugify } from '../create-spec-helpers.js';
/** Scan existing specs directory to collect current spec IDs. */
async function collectExistingSpecIds(specsDir) {
    try {
        const entries = await readdir(specsDir, { withFileTypes: true });
        return entries
            .filter((e) => e.isDirectory() && e.name.startsWith('SPEC-'))
            .map((e) => {
            const match = /^(SPEC-\d+)/.exec(e.name);
            /* v8 ignore next -- regex always matches SPEC- prefix from filter */
            return { id: match?.[1] ?? '' };
        })
            .filter((s) => s.id !== '');
        /* v8 ignore start -- directory may not exist */
    }
    catch {
        return [];
    }
    /* v8 ignore stop */
}
/** Inject registry source metadata into spec.md frontmatter. */
function injectRegistrySource(content, org, name, version) {
    const frontmatterEnd = content.indexOf('\n---', 3);
    if (frontmatterEnd === -1) {
        return content;
    }
    const insertion = `\nregistry:\n  source: "${org}/${name}"\n  version: "${version}"`;
    return content.slice(0, frontmatterEnd) + insertion + content.slice(frontmatterEnd);
}
function mergeLegacyRegistryFiles(files) {
    const specContent = files['spec.md'] ?? '';
    const sections = [specContent.trimEnd()];
    const technical = files['technical.md'] ?? files['FICHA-TECNICA.md'];
    if (technical !== undefined && technical.trim().length > 0) {
        sections.push('## Technical\n\n' + stripLeadingTitleAndFrontmatter(technical).trim());
    }
    const progress = files['progress.md'] ?? files['PROGRESS.md'];
    if (progress !== undefined && progress.trim().length > 0) {
        sections.push('## Progress\n\n' + stripLeadingTitleAndFrontmatter(progress).trim());
    }
    return sections.join('\n\n') + '\n';
}
function stripLeadingTitleAndFrontmatter(content) {
    return content.replace(/^---\n[\s\S]*?\n---\n/, '').replace(/^#\s+.*(?:\r?\n)+/, '');
}
function isInstallableRegistrySidecar(filePath) {
    return filePath === 'planu-registry.json';
}
/**
 * Install a spec from raw YAML content (HTTP backend path).
 * Writes one unified spec.md with registry source injected, then attempts stack adaptation.
 */
async function installFromYaml(yamlContent, org, name, version, projectPath) {
    const specsDir = join(projectPath, 'planu', 'specs');
    const existingSpecs = await collectExistingSpecIds(specsDir);
    const specId = generateSpecId(existingSpecs);
    const slug = slugify(name);
    const specDir = join(specsDir, `${specId}-${slug}`);
    await mkdir(specDir, { recursive: true });
    const specContent = injectRegistrySource(yamlContent, org, name, version);
    await writeFile(join(specDir, 'spec.md'), specContent, 'utf-8');
    const source = `${org}/${name}`;
    let adapted = false;
    let adaptationsCount = 0;
    try {
        const { detectStack, adaptSpec } = await import('../../engine/spec-registry/index.js');
        const stack = await detectStack(projectPath);
        const adaptations = await adaptSpec(specDir, stack);
        if (adaptations.length > 0) {
            adapted = true;
            adaptationsCount = adaptations.length;
        }
    }
    catch {
        // Adaptation failure should not block install
    }
    const lines = [
        `Done: Installed ${source}@${version} as ${specId}`,
        `**specId**: ${specId}`,
        `**source**: ${source}`,
        `**version**: ${version}`,
        `**specDir**: ${specDir}`,
        `**adapted**: ${String(adapted)}`,
    ];
    if (adapted) {
        lines.push(`**adaptations**: ${adaptationsCount}`);
    }
    return compactResult(lines.join('\n'));
}
/**
 * Handle registry_install tool invocation.
 * Downloads a spec from the registry and installs it locally.
 * Tries the HTTP client first, falls back to the tarball client stub if unavailable.
 */
export async function handleRegistryInstall(args) {
    try {
        const org = args.org;
        const name = args.name;
        const version = args.version ?? 'latest';
        const projectPath = args.projectPath ?? process.cwd();
        if (!org || !name) {
            return {
                content: [{ type: 'text', text: 'Missing required fields: org and name' }],
                isError: true,
            };
        }
        // Try real HTTP backend first; fall back to stub client if unavailable
        const { fetchRegistrySpec } = await import('../../engine/registry/http-client.js');
        const yamlContent = await fetchRegistrySpec(`${org}/${name}`, version);
        if (yamlContent !== null) {
            // HTTP backend available — write YAML directly without tarball flow
            return await installFromYaml(yamlContent, org, name, version, projectPath);
        }
        const { createRegistryClient, unpackSpec } = await import('../../engine/spec-registry/index.js');
        const client = createRegistryClient();
        const tarball = await client.downloadSpec(org, name, version);
        // Unpack to temp dir to read files
        const tmpDir = join(projectPath, '.planu-tmp-install');
        await mkdir(tmpDir, { recursive: true });
        await unpackSpec(tarball, tmpDir);
        const { readdir: rd, readFile: rf } = await import('node:fs/promises');
        const tmpFiles = await rd(tmpDir);
        const pkg = { files: {} };
        for (const f of tmpFiles) {
            pkg.files[f] = await rf(join(tmpDir, f), 'utf-8');
        }
        // Cleanup temp dir
        const { rm } = await import('node:fs/promises');
        await rm(tmpDir, { recursive: true, force: true });
        // Determine next spec ID
        const specsDir = join(projectPath, 'planu', 'specs');
        const existingSpecs = await collectExistingSpecIds(specsDir);
        const specId = generateSpecId(existingSpecs);
        const slug = slugify(name);
        const specDir = join(specsDir, `${specId}-${slug}`);
        // Create spec directory
        await mkdir(specDir, { recursive: true });
        // Write a unified spec.md. Legacy registry sidecars are folded into sections
        // instead of being materialized as standalone files in the spec directory.
        const source = `${org}/${name}`;
        const specContent = injectRegistrySource(mergeLegacyRegistryFiles(pkg.files), org, name, version);
        await writeFile(join(specDir, 'spec.md'), specContent, 'utf-8');
        for (const [filePath, content] of Object.entries(pkg.files)) {
            if (isInstallableRegistrySidecar(filePath)) {
                await writeFile(join(specDir, filePath), content, 'utf-8');
            }
        }
        // Attempt stack detection and spec adaptation (non-blocking on failure)
        let adapted = false;
        let adaptationsCount = 0;
        try {
            const { detectStack, adaptSpec } = await import('../../engine/spec-registry/index.js');
            const stack = await detectStack(projectPath);
            const adaptations = await adaptSpec(specDir, stack);
            if (adaptations.length > 0) {
                adapted = true;
                adaptationsCount = adaptations.length;
            }
        }
        catch {
            // Adaptation failure should not block install
        }
        const result = {
            specId,
            slug,
            specDir,
            source,
            version,
            adapted,
            ...(adapted ? { adaptations: adaptationsCount } : {}),
        };
        const lines2 = [
            `Done: Installed ${result.source}@${result.version} as ${result.specId}`,
            `**specId**: ${result.specId}`,
            `**source**: ${result.source}`,
            `**version**: ${result.version}`,
            `**specDir**: ${result.specDir}`,
            `**adapted**: ${String(result.adapted)}`,
        ];
        if (result.adapted) {
            lines2.push(`**adaptations**: ${String(result.adaptations ?? 0)}`);
        }
        return compactResult(lines2.join('\n'));
        /* v8 ignore start -- error + helper branches */
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        return {
            content: [{ type: 'text', text: `Registry install failed: ${message}` }],
            isError: true,
        };
    }
    /* v8 ignore stop */
}
//# sourceMappingURL=install.js.map