// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import fg from 'fast-glob';
import { format as formatWithPrettier, resolveConfig as resolvePrettierConfig } from 'prettier';
import YAML from 'yaml';
import { createError, ErrorCodes } from '@lumenflow/core/error-handler';
import { updateMergeBlock } from './merge-block.js';
import { runCLI } from './cli-entry-point.js';
const UTF8_ENCODING = 'utf-8';
const DEFAULT_PACKS_ROOT = path.join('packages', '@lumenflow', 'packs');
const DEFAULT_DOCS_ROOT = path.join('apps', 'docs', 'src', 'content', 'docs', 'packs');
const MANIFEST_GLOB = '*/manifest.yaml';
const EVENTS_SECTION = 'events';
const TOOLS_SECTION = 'tools';
const PAGE_FILE_NAME = 'index.mdx';
const GENERATED_WITH_COMMAND = 'pnpm docs:generate:pack-reference';
const PACK_TITLES = {
    'agent-runtime': 'Agent Runtime',
    sidekick: 'Sidekick',
    'software-delivery': 'Software Delivery',
};
const SOFTWARE_DELIVERY_EVENT_KINDS = {
    GATE_STARTED: 'software-delivery:gate_started',
    GATE_PASSED: 'software-delivery:gate_passed',
    GATE_FAILED: 'software-delivery:gate_failed',
    CO_CHANGE_RULE_FIRED: 'software-delivery:co_change_rule_fired',
    CONDITIONAL_COMMAND_FIRED: 'software-delivery:conditional_command_fired',
    LANE_WIP_BREACHED: 'software-delivery:lane_wip_breached',
    LANE_LOCKED: 'software-delivery:lane_locked',
    LANE_UNLOCKED: 'software-delivery:lane_unlocked',
    LANE_SUGGESTION_EMITTED: 'software-delivery:lane_suggestion_emitted',
    INITIATIVE_CREATED: 'software-delivery:initiative_created',
    INITIATIVE_CLOSED: 'software-delivery:initiative_closed',
    INITIATIVE_PHASE_ADVANCED: 'software-delivery:initiative_phase_advanced',
    PLAN_CREATED: 'software-delivery:plan_created',
    PLAN_LINKED: 'software-delivery:plan_linked',
    PLAN_PROMOTED: 'software-delivery:plan_promoted',
};
const SOFTWARE_DELIVERY_EVENT_DOCS = {
    [SOFTWARE_DELIVERY_EVENT_KINDS.GATE_STARTED]: {
        payloadShape: '{ wu_id, lane, gate_name, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildGateStartedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.GATE_PASSED]: {
        payloadShape: '{ wu_id, lane, gate_name, duration_ms, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildGatePassedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.GATE_FAILED]: {
        payloadShape: '{ wu_id, lane, gate_name, duration_ms, reason, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildGateFailedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.CO_CHANGE_RULE_FIRED]: {
        payloadShape: '{ wu_id, rule_id, triggered_paths, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildCoChangeRuleFiredEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.CONDITIONAL_COMMAND_FIRED]: {
        payloadShape: '{ wu_id, command_id, trigger, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildConditionalCommandFiredEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.LANE_WIP_BREACHED]: {
        payloadShape: '{ lane, wip_limit, current_wip, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildLaneWipBreachedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.LANE_LOCKED]: {
        payloadShape: '{ lane, held_by, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildLaneLockedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.LANE_UNLOCKED]: {
        payloadShape: '{ lane, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildLaneUnlockedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.LANE_SUGGESTION_EMITTED]: {
        payloadShape: '{ wu_id, suggested_lane, confidence, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildLaneSuggestionEmittedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.INITIATIVE_CREATED]: {
        payloadShape: '{ initiative, wu_count, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildInitiativeCreatedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.INITIATIVE_CLOSED]: {
        payloadShape: '{ initiative, reason, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildInitiativeClosedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.INITIATIVE_PHASE_ADVANCED]: {
        payloadShape: '{ initiative, from_phase, to_phase, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildInitiativePhaseAdvancedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.PLAN_CREATED]: {
        payloadShape: '{ plan_id, initiative, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildPlanCreatedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.PLAN_LINKED]: {
        payloadShape: '{ plan_id, wu_id, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildPlanLinkedEvent(',
    },
    [SOFTWARE_DELIVERY_EVENT_KINDS.PLAN_PROMOTED]: {
        payloadShape: '{ plan_id, promoted_wu_id, channel_id, seq }',
        backpressurePolicy: 'ephemeral',
        emissionSearchTerm: 'buildPlanPromotedEvent(',
    },
};
const CODE_GLOBS = ['packages/@lumenflow/cli/src/**/*.ts', 'packages/@lumenflow/cli/src/**/*.tsx'];
const CODE_IGNORE = ['**/__tests__/**', '**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts'];
const EMITTER_DEFINITION_FILE = path.join('packages', '@lumenflow', 'cli', 'src', 'kernel-event-sync', 'software-delivery-emitters.ts');
const GENERATOR_SOURCE_FILE = path.join('packages', '@lumenflow', 'cli', 'src', 'docs-generate-pack-reference.ts');
function formatUsage() {
    return [
        'Usage: docs:generate:pack-reference [options]',
        '',
        'Generate Starlight /packs/<pack>/events/ and /tools/ reference pages from pack manifests.',
        '',
        'Options:',
        '  --help   Show this help message',
    ].join('\n');
}
function resolvePackTitle(packId) {
    return PACK_TITLES[packId] ?? packId;
}
function resolveProjectRoot(projectRoot) {
    return path.resolve(projectRoot ?? process.cwd());
}
function resolvePacksRoot(projectRoot, packsRoot) {
    return path.resolve(projectRoot, packsRoot ?? DEFAULT_PACKS_ROOT);
}
function resolveDocsRoot(projectRoot, docsRoot) {
    return path.resolve(projectRoot, docsRoot ?? DEFAULT_DOCS_ROOT);
}
function relativeToProject(projectRoot, targetPath) {
    return path.relative(projectRoot, targetPath).split(path.sep).join('/');
}
function loadPackManifest(manifestPath) {
    const rawContent = readFileSync(manifestPath, UTF8_ENCODING);
    return parseManifestRecord(YAML.parse(rawContent), manifestPath);
}
function sortManifestPaths(paths) {
    return [...paths].sort((left, right) => left.localeCompare(right));
}
function discoverManifestPaths(packsRoot) {
    return sortManifestPaths(fg.sync(MANIFEST_GLOB, {
        cwd: packsRoot,
        absolute: true,
        onlyFiles: true,
    }));
}
function stringifyJsonBlock(schema) {
    if (!schema || typeof schema !== 'object') {
        return 'Not declared in the pack manifest.';
    }
    return `\`\`\`json\n${JSON.stringify(schema, null, 2)}\n\`\`\``;
}
function formatScope(scope) {
    if (scope.type === 'path') {
        return `\`path ${scope.access} ${scope.pattern}\``;
    }
    if (scope.type === 'network') {
        if (scope.posture === 'allowlist') {
            const entries = scope.allowlist_entries?.map((entry) => `\`${entry}\``).join(', ') ?? '[]';
            return `\`network allowlist\` (${entries})`;
        }
        return `\`network ${scope.posture}\``;
    }
    return `\`${JSON.stringify(scope)}\``;
}
function formatApprovals(approvals) {
    if (approvals.length === 0) {
        return 'None';
    }
    return approvals.map((approval) => `\`${approval}\``).join(', ');
}
function buildToolSummaryTable(manifest) {
    const rows = manifest.tools
        .map((tool) => {
        const approvals = formatApprovals(tool.required_approvals ?? []);
        const scopes = tool.required_scopes.map(formatScope).join('<br />');
        return `| \`${tool.name}\` | \`${tool.permission}\` | ${approvals} | ${scopes} |`;
    })
        .join('\n');
    return [
        '## Tool Summary',
        '',
        '| Tool | Permission | Required approvals | Required scopes |',
        '| --- | --- | --- | --- |',
        rows,
    ].join('\n');
}
function buildToolDetails(manifest) {
    if (manifest.tools.length === 0) {
        return '## Tool Details\n\nNo tools are declared in this pack manifest.';
    }
    const sections = manifest.tools.map((tool) => [
        `### \`${tool.name}\``,
        '',
        `- Permission: \`${tool.permission}\``,
        `- Required approvals: ${formatApprovals(tool.required_approvals ?? [])}`,
        `- Required scopes: ${tool.required_scopes.map(formatScope).join(', ')}`,
        '',
        '#### Input schema',
        '',
        stringifyJsonBlock(tool.input_schema),
        '',
        '#### Output schema',
        '',
        stringifyJsonBlock(tool.output_schema),
    ].join('\n'));
    return ['## Tool Details', '', ...sections].join('\n\n');
}
function asRecord(value, label) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        throw createError(ErrorCodes.PARSE_ERROR, `Failed to parse ${label}: expected an object.`);
    }
    return value;
}
function asString(value, label) {
    if (typeof value !== 'string' || value.trim().length === 0) {
        throw createError(ErrorCodes.PARSE_ERROR, `Failed to parse ${label}: expected a non-empty string.`);
    }
    return value;
}
function asStringArray(value, label) {
    if (value === undefined) {
        return [];
    }
    if (!Array.isArray(value)) {
        throw createError(ErrorCodes.PARSE_ERROR, `Failed to parse ${label}: expected an array.`);
    }
    return value.map((entry, index) => asString(entry, `${label}[${index}]`));
}
function parseScope(input, label) {
    const scope = asRecord(input, label);
    const scopeType = asString(scope.type, `${label}.type`);
    if (scopeType === 'path') {
        return {
            type: 'path',
            pattern: asString(scope.pattern, `${label}.pattern`),
            access: asString(scope.access, `${label}.access`),
        };
    }
    if (scopeType === 'network') {
        return {
            type: 'network',
            posture: asString(scope.posture, `${label}.posture`),
            allowlist_entries: asStringArray(scope.allowlist_entries, `${label}.allowlist_entries`),
        };
    }
    throw createError(ErrorCodes.PARSE_ERROR, `Failed to parse ${label}: unsupported scope type "${scopeType}".`);
}
function parseTool(input, label) {
    const tool = asRecord(input, label);
    const requiredScopesValue = tool.required_scopes;
    if (!Array.isArray(requiredScopesValue)) {
        throw createError(ErrorCodes.PARSE_ERROR, `Failed to parse ${label}.required_scopes: expected an array.`);
    }
    return {
        name: asString(tool.name, `${label}.name`),
        permission: asString(tool.permission ?? 'read', `${label}.permission`),
        required_scopes: requiredScopesValue.map((scope, index) => parseScope(scope, `${label}.required_scopes[${index}]`)),
        input_schema: tool.input_schema,
        output_schema: tool.output_schema,
        required_approvals: asStringArray(tool.required_approvals, `${label}.required_approvals`),
    };
}
function parseManifestRecord(value, manifestPath) {
    const manifest = asRecord(value, manifestPath);
    const toolsValue = manifest.tools ?? [];
    if (!Array.isArray(toolsValue)) {
        throw createError(ErrorCodes.PARSE_ERROR, `Failed to parse ${manifestPath}: tools must be an array.`);
    }
    return {
        id: asString(manifest.id, `${manifestPath}: id`),
        tools: toolsValue.map((tool, index) => parseTool(tool, `${manifestPath}: tools[${index}]`)),
        emitted_event_kinds: asStringArray(manifest.emitted_event_kinds, `${manifestPath}: emitted_event_kinds`),
        subscribed_event_kinds: asStringArray(manifest.subscribed_event_kinds, `${manifestPath}: subscribed_event_kinds`),
    };
}
function lookupEventDocs(eventKind) {
    const metadata = SOFTWARE_DELIVERY_EVENT_DOCS[eventKind];
    if (metadata) {
        return metadata;
    }
    return {
        payloadShape: 'Manifest-declared kind; typed payload summary is not yet published for this pack.',
        backpressurePolicy: 'not declared',
    };
}
function buildCodeIndex(projectRoot) {
    const files = fg.sync(CODE_GLOBS, {
        cwd: projectRoot,
        absolute: true,
        onlyFiles: true,
        ignore: CODE_IGNORE,
    });
    return files
        .map((filePath) => ({
        relativePath: relativeToProject(projectRoot, filePath),
        lines: readFileSync(filePath, UTF8_ENCODING).split('\n'),
    }))
        .filter((entry) => entry.relativePath !== EMITTER_DEFINITION_FILE &&
        entry.relativePath !== GENERATOR_SOURCE_FILE);
}
function findEmissionSites(codeIndex, searchTerm) {
    if (!searchTerm) {
        return [];
    }
    const matches = [];
    for (const file of codeIndex) {
        file.lines.forEach((line, index) => {
            if (line.includes(searchTerm)) {
                matches.push(`${file.relativePath}:${index + 1}`);
            }
        });
    }
    return matches;
}
function buildEventRows(manifest, codeIndex) {
    const emittedRows = manifest.emitted_event_kinds.map((kind) => {
        const metadata = lookupEventDocs(kind);
        return {
            kind,
            direction: 'emitted',
            payloadShape: metadata.payloadShape,
            backpressurePolicy: metadata.backpressurePolicy,
            emissionSites: findEmissionSites(codeIndex, metadata.emissionSearchTerm),
        };
    });
    const subscribedRows = manifest.subscribed_event_kinds.map((kind) => ({
        kind,
        direction: 'subscribed',
        payloadShape: 'Inbound handler surface declared by the pack manifest.',
        backpressurePolicy: 'handled by subscriber',
        emissionSites: [],
    }));
    return [...emittedRows, ...subscribedRows];
}
function renderEmissionSitesTableCell(emissionSites) {
    if (emissionSites.length === 0) {
        return 'Not found';
    }
    return emissionSites.map((site) => `\`${site}\``).join('<br />');
}
function buildEventSummary(rows) {
    if (rows.length === 0) {
        return '## Event Summary\n\nNo manifest-declared event kinds for this pack yet.';
    }
    const tableRows = rows
        .map((row) => `| \`${row.kind}\` | \`${row.direction}\` | ${row.payloadShape} | \`${row.backpressurePolicy}\` | ${renderEmissionSitesTableCell(row.emissionSites)} |`)
        .join('\n');
    return [
        '## Event Summary',
        '',
        '| Kind | Direction | Payload shape | Backpressure policy | Emission sites |',
        '| --- | --- | --- | --- | --- |',
        tableRows,
    ].join('\n');
}
function buildEventDetails(rows) {
    if (rows.length === 0) {
        return '';
    }
    const sections = rows.map((row) => [
        `### \`${row.kind}\``,
        '',
        `- Direction: \`${row.direction}\``,
        `- Payload shape: ${row.payloadShape}`,
        `- Backpressure policy: \`${row.backpressurePolicy}\``,
        `- Emission sites: ${row.emissionSites.length > 0
            ? row.emissionSites.map((site) => `\`${site}\``).join(', ')
            : 'Not found in source scan'}`,
    ].join('\n'));
    return ['## Event Details', '', ...sections].join('\n\n');
}
function buildGeneratedPageBody(sections) {
    return sections.filter((section) => section.trim().length > 0).join('\n\n');
}
async function formatReferencePageContent(content, pagePath) {
    const prettierConfig = (await resolvePrettierConfig(pagePath)) ?? {};
    return formatWithPrettier(content, {
        ...prettierConfig,
        filepath: pagePath,
    });
}
function createPageScaffold(title, description) {
    return [
        '---',
        `title: ${title}`,
        `description: ${description}`,
        '---',
        '',
        `This page is generated by \`${GENERATED_WITH_COMMAND}\`. Add any pack-specific notes outside the managed block below; reruns preserve them.`,
        '',
    ].join('\n');
}
async function updateReferencePage(options) {
    const { projectRoot, docsRoot, manifest, section, generatedBody } = options;
    const titlePrefix = resolvePackTitle(manifest.id);
    const pageTitle = section === EVENTS_SECTION ? `${titlePrefix} Events` : `${titlePrefix} Tools`;
    const pageDescription = section === EVENTS_SECTION
        ? `Auto-generated event reference for the ${titlePrefix} Pack`
        : `Auto-generated tool catalog for the ${titlePrefix} Pack`;
    const pagePath = path.join(docsRoot, manifest.id, section, PAGE_FILE_NAME);
    const relativePath = relativeToProject(projectRoot, pagePath);
    const fileExisted = existsSync(pagePath);
    mkdirSync(path.dirname(pagePath), { recursive: true });
    const existingContent = fileExisted
        ? readFileSync(pagePath, UTF8_ENCODING)
        : createPageScaffold(pageTitle, pageDescription);
    const mergeResult = updateMergeBlock(existingContent, generatedBody);
    const formattedContent = await formatReferencePageContent(mergeResult.content, pagePath);
    if (formattedContent === existingContent) {
        return { relativePath, status: 'unchanged' };
    }
    writeFileSync(pagePath, formattedContent, UTF8_ENCODING);
    return {
        relativePath,
        status: fileExisted ? 'updated' : 'created',
    };
}
function pushResult(result, update) {
    if (update.status === 'created') {
        result.created.push(update.relativePath);
        return;
    }
    if (update.status === 'updated') {
        result.updated.push(update.relativePath);
        return;
    }
    result.unchanged.push(update.relativePath);
}
export async function generatePackReferenceDocs(options = {}) {
    const projectRoot = resolveProjectRoot(options.projectRoot);
    const packsRoot = resolvePacksRoot(projectRoot, options.packsRoot);
    const docsRoot = resolveDocsRoot(projectRoot, options.docsRoot);
    const manifestPaths = discoverManifestPaths(packsRoot);
    const codeIndex = buildCodeIndex(projectRoot);
    const result = {
        created: [],
        updated: [],
        unchanged: [],
    };
    for (const manifestPath of manifestPaths) {
        const manifest = loadPackManifest(manifestPath);
        const eventRows = buildEventRows(manifest, codeIndex);
        const eventsBody = buildGeneratedPageBody([
            buildEventSummary(eventRows),
            buildEventDetails(eventRows),
        ]);
        const toolsBody = buildGeneratedPageBody([
            buildToolSummaryTable(manifest),
            buildToolDetails(manifest),
        ]);
        pushResult(result, await updateReferencePage({
            projectRoot,
            docsRoot,
            manifest,
            section: EVENTS_SECTION,
            generatedBody: eventsBody,
        }));
        pushResult(result, await updateReferencePage({
            projectRoot,
            docsRoot,
            manifest,
            section: TOOLS_SECTION,
            generatedBody: toolsBody,
        }));
    }
    return result;
}
export async function main(argv = process.argv.slice(2)) {
    if (argv.includes('--help')) {
        console.log(formatUsage());
        return;
    }
    if (argv.length > 0) {
        throw createError(ErrorCodes.INVALID_ARGUMENT, `Unknown option(s): ${argv.join(', ')}\n\n${formatUsage()}`);
    }
    const result = await generatePackReferenceDocs();
    console.log('[docs:generate:pack-reference] Pack reference generation complete.');
    console.log(`  Created: ${result.created.length}`);
    console.log(`  Updated: ${result.updated.length}`);
    console.log(`  Unchanged: ${result.unchanged.length}`);
}
if (import.meta.main) {
    void runCLI(main);
}
//# sourceMappingURL=docs-generate-pack-reference.js.map