// engine/spec-template-engine.ts — Template engine for SPEC-320 Spec Marketplace
import { readFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
// ---------------------------------------------------------------------------
// Path resolution
// ---------------------------------------------------------------------------
const __dirname = dirname(fileURLToPath(import.meta.url));
const BUILTIN_TEMPLATES_DIR = join(__dirname, '..', 'config', 'spec-templates');
const BUILTIN_TEMPLATE_NAMES = [
    'stripe-payments',
    'oauth-social-login',
    'crud-rest-api',
    'file-upload-s3',
    'jwt-auth',
    'rate-limiting',
    'email-notifications',
    'webhook-system',
];
// ---------------------------------------------------------------------------
// Template loading
// ---------------------------------------------------------------------------
async function loadBuiltinManifest(name) {
    const manifestPath = join(BUILTIN_TEMPLATES_DIR, name, 'template.json');
    if (!existsSync(manifestPath)) {
        return null;
    }
    const raw = await readFile(manifestPath, 'utf-8');
    return JSON.parse(raw);
}
async function loadBuiltinSpec(name) {
    const specPath = join(BUILTIN_TEMPLATES_DIR, name, 'spec.md');
    if (!existsSync(specPath)) {
        return '';
    }
    return readFile(specPath, 'utf-8');
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * List all available templates (builtin only for now).
 */
export async function listTemplates() {
    const results = [];
    for (const name of BUILTIN_TEMPLATE_NAMES) {
        const manifest = await loadBuiltinManifest(name);
        if (manifest !== null) {
            results.push({ manifest, source: 'builtin' });
        }
    }
    return results;
}
/**
 * Search templates by name, description, or tags. Returns all templates when query is empty.
 */
export async function searchTemplates(query) {
    const all = await listTemplates();
    if (query.trim().length === 0) {
        return all;
    }
    const lower = query.toLowerCase();
    return all.filter((t) => {
        const nameMatch = t.manifest.name.toLowerCase().includes(lower);
        const descMatch = t.manifest.description.toLowerCase().includes(lower);
        const tagMatch = t.manifest.tags.some((tag) => tag.toLowerCase().includes(lower));
        return nameMatch || descMatch || tagMatch;
    });
}
/**
 * Apply variable substitution to template content.
 * Replaces all occurrences of {{variable_name}} with the provided value.
 * Missing variables fall back to the variable's default, then to the placeholder.
 */
export function applyVariables(content, variables, variableDefs) {
    let result = content;
    for (const varDef of variableDefs) {
        const value = variables[varDef.name] ?? varDef.default ?? `{{${varDef.name}}}`;
        result = result.replaceAll(`{{${varDef.name}}}`, value);
    }
    return result;
}
/**
 * Instantiate a template by name with the given variables.
 * Returns the rendered spec.md content, or null if the template does not exist.
 */
export async function applyTemplate(templateName, variables) {
    const manifest = await loadBuiltinManifest(templateName);
    if (manifest === null) {
        return null;
    }
    const specContent = await loadBuiltinSpec(templateName);
    return applyVariables(specContent, variables, manifest.variables);
}
/**
 * Build a MarketplaceTemplateManifest from a spec's content and the provided variable names.
 * Used for the "publish" flow.
 */
export function buildPublishManifest(specId, templateName, variableNames, specContent) {
    const variables = variableNames.map((v) => {
        const placeholder = `{{${v}}}`;
        const inContent = specContent.includes(placeholder);
        return {
            name: v,
            description: inContent ? 'Variable used in spec content' : 'Custom variable',
        };
    });
    const tagLine = specContent.split('\n').find((line) => line.toLowerCase().startsWith('tags:'));
    const tags = tagLine !== undefined
        ? tagLine
            .replace(/^tags:/i, '')
            .trim()
            .split(',')
            .map((t) => t.trim())
            .filter(Boolean)
        : [specId];
    return {
        name: templateName,
        description: `Template published from spec ${specId}`,
        variables,
        tags,
    };
}
//# sourceMappingURL=spec-template-engine.js.map