/**
 * Manifest generation — converts parsed ASTs into a flowweaver.manifest.json file.
 *
 * The manifest mirrors AST types so the editor can consume it directly
 * without re-parsing source files.
 */
import * as fs from 'fs';
import * as path from 'path';
import { glob } from 'glob';
import { AnnotationParser } from '../parser.js';
// ── Port mapping ─────────────────────────────────────────────────────────────
function toManifestPort(port) {
    return {
        dataType: port.dataType,
        ...(port.description && { description: port.description }),
        ...(port.optional && { optional: true }),
    };
}
function toManifestPortFromPattern(port) {
    return {
        dataType: port.dataType ?? 'ANY',
        ...(port.description && { description: port.description }),
    };
}
function toManifestPorts(ports) {
    const result = {};
    for (const [name, def] of Object.entries(ports)) {
        result[name] = toManifestPort(def);
    }
    return result;
}
// ── AST → Manifest mapping ──────────────────────────────────────────────────
function nodeTypeToManifest(nt, relativeFile) {
    return {
        name: nt.name,
        ...(nt.description && { description: nt.description }),
        file: relativeFile,
        functionName: nt.functionName,
        isAsync: nt.isAsync,
        inputs: toManifestPorts(nt.inputs),
        outputs: toManifestPorts(nt.outputs),
        ...(nt.visuals && {
            visuals: {
                ...(nt.visuals.color && { color: nt.visuals.color }),
                ...(nt.visuals.icon && { icon: nt.visuals.icon }),
                ...(nt.visuals.tags && { tags: nt.visuals.tags }),
            },
        }),
    };
}
function workflowToManifest(wf, relativeFile) {
    return {
        name: wf.name,
        ...(wf.description && { description: wf.description }),
        file: relativeFile,
        functionName: wf.functionName,
        startPorts: toManifestPorts(wf.startPorts),
        exitPorts: toManifestPorts(wf.exitPorts),
        nodeCount: wf.instances.length,
        connectionCount: wf.connections.length,
    };
}
function patternToManifest(pat, relativeFile) {
    const inputPorts = {};
    for (const [name, def] of Object.entries(pat.inputPorts)) {
        inputPorts[name] = toManifestPortFromPattern(def);
    }
    const outputPorts = {};
    for (const [name, def] of Object.entries(pat.outputPorts)) {
        outputPorts[name] = toManifestPortFromPattern(def);
    }
    return {
        name: pat.name,
        ...(pat.description && { description: pat.description }),
        file: relativeFile,
        inputPorts,
        outputPorts,
        nodeCount: pat.instances.length,
    };
}
/**
 * Generate a marketplace manifest by parsing all annotated source files
 * in the package directory.
 */
export async function generateManifest(options) {
    const { directory, srcDir = 'src', distDir = 'dist' } = options;
    // Read package.json
    const pkgPath = path.join(directory, 'package.json');
    if (!fs.existsSync(pkgPath)) {
        return {
            manifest: emptyManifest('unknown', '0.0.0'),
            parsedFiles: [],
            errors: ['package.json not found'],
        };
    }
    const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
    const srcRoot = path.join(directory, srcDir);
    // Find all TypeScript files in src/
    const pattern = path.join(srcRoot, '**/*.ts').replace(/\\/g, '/');
    const files = await glob(pattern, { absolute: true });
    const tsFiles = files.filter((f) => !f.endsWith('.d.ts') && !f.includes('node_modules'));
    const parser = new AnnotationParser();
    const allNodeTypes = [];
    const allWorkflows = [];
    const allPatterns = [];
    const errors = [];
    const parsedFiles = [];
    for (const file of tsFiles) {
        try {
            const result = parser.parse(file);
            parsedFiles.push(file);
            // Map source paths to dist paths: src/foo/bar.ts → dist/foo/bar.js
            const relFromSrc = path.relative(srcRoot, file);
            const distRelative = path.join(distDir, relFromSrc.replace(/\.ts$/, '.js'));
            // Collect standalone node types (not embedded in workflows)
            for (const nt of result.nodeTypes) {
                allNodeTypes.push(nodeTypeToManifest(nt, distRelative));
            }
            // Collect workflows
            for (const wf of result.workflows) {
                allWorkflows.push(workflowToManifest(wf, distRelative));
            }
            // Collect patterns
            for (const pat of result.patterns) {
                allPatterns.push(patternToManifest(pat, distRelative));
            }
            if (result.errors.length > 0) {
                errors.push(...result.errors.map((e) => `${file}: ${e}`));
            }
        }
        catch (err) {
            errors.push(`Failed to parse ${file}: ${err instanceof Error ? err.message : String(err)}`);
        }
    }
    // Preserve v2 extension fields from the existing manifest so that
    // re-running pack:manifest doesn't wipe manually declared tagHandlers,
    // validationRuleSets, docs, exportTargets, initContributions, etc.
    const existing = readManifest(directory);
    const v2Fields = {};
    if (existing) {
        if (existing.tagHandlers)
            v2Fields.tagHandlers = existing.tagHandlers;
        if (existing.validationRuleSets)
            v2Fields.validationRuleSets = existing.validationRuleSets;
        if (existing.exportTargets)
            v2Fields.exportTargets = existing.exportTargets;
        if (existing.docs)
            v2Fields.docs = existing.docs;
        if (existing.initContributions)
            v2Fields.initContributions = existing.initContributions;
    }
    const manifest = {
        manifestVersion: 2,
        name: pkg.name ?? 'unknown',
        version: pkg.version ?? '0.0.0',
        ...(pkg.description && { description: pkg.description }),
        ...(pkg.flowWeaver?.engineVersion && { engineVersion: pkg.flowWeaver.engineVersion }),
        ...(pkg.flowWeaver?.categories && { categories: pkg.flowWeaver.categories }),
        nodeTypes: allNodeTypes,
        workflows: allWorkflows,
        patterns: allPatterns,
        ...v2Fields,
        ...(Object.keys(pkg.dependencies ?? {}).length > 0 && {
            dependencies: {
                npm: pkg.dependencies,
            },
        }),
    };
    return { manifest, parsedFiles, errors };
}
/** Write manifest JSON to disk. */
export function writeManifest(directory, manifest) {
    const outPath = path.join(directory, 'flowweaver.manifest.json');
    fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
    return outPath;
}
/** Read manifest JSON from disk. */
export function readManifest(directory) {
    const manifestPath = path.join(directory, 'flowweaver.manifest.json');
    if (!fs.existsSync(manifestPath))
        return null;
    return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
}
function emptyManifest(name, version) {
    return {
        manifestVersion: 2,
        name,
        version,
        nodeTypes: [],
        workflows: [],
        patterns: [],
    };
}
//# sourceMappingURL=manifest.js.map