"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.COMMAND_RUNTIME_SEMANTICS_PROPERTY = void 0;
exports.attachCommandRuntimeSemantics = attachCommandRuntimeSemantics;
exports.buildCommandInventory = buildCommandInventory;
exports.collectCallableCommandEvidence = collectCallableCommandEvidence;
exports.collectRuntimeCommandEvidence = collectRuntimeCommandEvidence;
exports.createCommandInventoryFindings = createCommandInventoryFindings;
exports.getCommandInventoryEntry = getCommandInventoryEntry;
exports.getCommandInventoryCapability = getCommandInventoryCapability;
const command_capabilities_1 = require("./command-capabilities");
const command_descriptors_1 = require("./command-descriptors");
const command_availability_1 = require("./command-availability");
const command_descriptor_projections_1 = require("./command-descriptor-projections");
const validation_1 = require("./validation");
exports.COMMAND_RUNTIME_SEMANTICS_PROPERTY = 'dayShiftSemanticContract';
function attachCommandRuntimeSemantics(commandNode, semantics) {
    commandNode[exports.COMMAND_RUNTIME_SEMANTICS_PROPERTY] = semantics;
}
const INVENTORY_NO_WRITE_BOUNDARY = 'Reports callable command and capability registry evidence only; it does not write files, repair registry gaps, or authorize follow-up mutation.';
function buildCommandInventory(program, registry = command_capabilities_1.COMMAND_DESCRIPTOR_REGISTRY) {
    const runtimeEvidence = collectRuntimeCommandEvidence(program);
    const runtimeByName = new Map(runtimeEvidence.map((entry) => [entry.command, entry]));
    const registryByName = new Map(registry.map((entry) => [entry.command, entry]));
    const commandNames = [
        ...new Set([...runtimeEvidence.map((entry) => entry.command), ...registry.map((entry) => entry.command)]),
    ].sort(compareCommandNames);
    const entries = commandNames.map((commandName) => buildInventoryEntry({
        runtime: runtimeByName.get(commandName) ?? null,
        capability: registryByName.get(commandName) ?? null,
    }));
    const coverage = buildInventoryCoverage(entries, registry);
    return Object.freeze({
        schemaVersion: 1,
        generatedFrom: ['commander-tree', 'command-capability-registry'],
        entries,
        coverage,
        noWriteBoundary: INVENTORY_NO_WRITE_BOUNDARY,
        readOnly: true,
    });
}
function collectCallableCommandEvidence(program) {
    return collectRuntimeCommandEvidence(program).filter((entry) => entry.runtimeActionHandler);
}
function collectRuntimeCommandEvidence(program) {
    const entries = [];
    for (const child of getSubcommands(program)) {
        collectRuntimeCommandEvidenceFromNode(child, null, entries);
    }
    return entries.sort((left, right) => compareCommandNames(left.command, right.command));
}
function createCommandInventoryFindings(coverage) {
    const findings = [];
    if (coverage.callableMissingCapability.length > 0) {
        findings.push((0, validation_1.createAdvisoryValidationFinding)({
            code: 'COMMAND_INVENTORY_CALLABLE_MISSING_CAPABILITY',
            message: `Callable commands missing capability metadata: ${coverage.callableMissingCapability.join(', ')}.`,
            advisoryReason: 'possible-discovered-work',
            recovery: 'Add command capability registry entries or explicitly classify the callable command surface.',
            evidence: {
                category: 'command-specific',
                details: {
                    ruleFamily: 'command-inventory-coverage',
                    evidenceSource: 'commander-tree',
                    commands: coverage.callableMissingCapability,
                },
            },
        }));
    }
    if (coverage.registryOnly.length > 0) {
        findings.push((0, validation_1.createAdvisoryValidationFinding)({
            code: 'COMMAND_INVENTORY_REGISTRY_ONLY',
            message: `Capability registry entries are not currently callable: ${coverage.registryOnly.join(', ')}.`,
            advisoryReason: 'weak-evidence',
            level: 'informational',
            recovery: 'Treat these entries as reserved or future capability metadata until a callable Commander command exists.',
            evidence: {
                category: 'command-specific',
                details: {
                    ruleFamily: 'command-inventory-coverage',
                    evidenceSource: 'command-capability-registry',
                    commands: coverage.registryOnly,
                },
            },
        }));
    }
    if (coverage.mixedPostureFamilies.length > 0) {
        findings.push((0, validation_1.createAdvisoryValidationFinding)({
            code: 'COMMAND_INVENTORY_MIXED_POSTURE_FAMILIES',
            message: `Mixed-posture command families require subcommand-level classification: ${coverage.mixedPostureFamilies.map((entry) => entry.command).join(', ')}.`,
            advisoryReason: 'weak-evidence',
            level: 'informational',
            recovery: 'Use subcommand entries for automation decisions instead of collapsing mixed families to one posture.',
            evidence: {
                category: 'command-specific',
                details: {
                    ruleFamily: 'command-inventory-coverage',
                    evidenceSource: 'command-capability-registry',
                },
            },
        }));
    }
    return findings;
}
function collectRuntimeCommandEvidenceFromNode(commandNode, parent, entries) {
    const name = safeCommandName(commandNode);
    if (name === null) {
        return;
    }
    const commandName = parent === null ? name : `${parent} ${name}`;
    const options = normalizeOptions(commandNode);
    const args = normalizeArguments(commandNode);
    entries.push(Object.freeze({
        command: commandName,
        parent,
        description: safeString(commandNode.description?.()),
        aliases: normalizeStringArray(commandNode.aliases?.()),
        arguments: args,
        options,
        selectedArtifactRequired: inferSelectedArtifactRequired(args, options),
        runtimeActionHandler: (0, command_availability_1.hasCommanderActionHandler)(commandNode),
        semantics: normalizeRuntimeSemantics(commandNode[exports.COMMAND_RUNTIME_SEMANTICS_PROPERTY]),
    }));
    for (const child of getSubcommands(commandNode)) {
        collectRuntimeCommandEvidenceFromNode(child, commandName, entries);
    }
}
function buildInventoryEntry(input) {
    const command = input.runtime?.command ?? input.capability?.command;
    if (command === undefined) {
        throw new Error('Cannot build command inventory entry without command identity.');
    }
    const descriptorFields = (0, command_descriptor_projections_1.projectCommandDescriptorInventoryFields)(input.capability);
    const executableEligibility = (0, command_availability_1.evaluateCommandExecutableEligibility)(input.capability, input.runtime?.runtimeActionHandler === true);
    return Object.freeze({
        command,
        availability: input.capability?.availability ?? null,
        parent: input.runtime?.parent ?? inferParentCommand(command),
        description: input.runtime?.description ?? '',
        aliases: input.runtime?.aliases ?? [],
        arguments: input.runtime?.arguments ?? [],
        options: input.runtime?.options ?? [],
        writeCapable: descriptorFields.writeCapable,
        capability: descriptorFields.capability,
        artifactFamily: descriptorFields.artifactFamily,
        mutationPosture: descriptorFields.mutationPosture,
        coordinationScope: descriptorFields.coordinationScope,
        targetPathPosture: descriptorFields.targetPathPosture,
        selectedArtifactRequired: input.capability?.selectionRequired === true || input.runtime?.selectedArtifactRequired === true,
        runtimeActionHandler: input.runtime?.runtimeActionHandler === true,
        callable: executableEligibility.executable,
        executableEligibility,
        evidenceSource: [
            ...(input.runtime === null ? [] : ['commander-tree']),
            ...(input.capability === null ? [] : ['command-capability-registry']),
        ],
        destinationPosture: descriptorFields.destinationPosture,
        summary: descriptorFields.summary,
        noWriteBoundary: descriptorFields.noWriteBoundary,
        descriptorSource: descriptorFields.descriptorSource,
        descriptorAliases: descriptorFields.descriptorAliases,
        descriptorInputs: descriptorFields.descriptorInputs,
        validationNotes: descriptorFields.validationNotes,
        examples: descriptorFields.examples,
        effects: descriptorFields.effects,
        authorization: descriptorFields.authorization,
        confirmation: descriptorFields.confirmation,
        idempotency: descriptorFields.idempotency,
        transition: descriptorFields.transition,
        semantics: input.capability?.semantics ?? null,
    });
}
function buildInventoryCoverage(entries, registry) {
    const runtimeActionMissingCallableDescriptor = entries
        .filter((entry) => entry.runtimeActionHandler && entry.availability !== 'callable')
        .map((entry) => entry.command);
    const callableDescriptorMissingRuntimeAction = entries
        .filter((entry) => entry.availability === 'callable' && !entry.runtimeActionHandler)
        .map((entry) => entry.command);
    const registryOnly = entries
        .filter((entry) => !entry.evidenceSource.includes('commander-tree'))
        .map((entry) => entry.command);
    return Object.freeze({
        callableCount: entries.filter((entry) => entry.callable).length,
        runtimeActionCount: entries.filter((entry) => entry.runtimeActionHandler).length,
        callableDescriptorCount: entries.filter((entry) => entry.availability === 'callable').length,
        registryCount: registry.length,
        mergedCount: entries.length,
        callableMissingCapability: runtimeActionMissingCallableDescriptor,
        callableDescriptorMissingRuntimeAction,
        runtimeActionMissingCallableDescriptor,
        intentionalNonCallable: entries
            .filter((entry) => entry.availability !== null && entry.availability !== 'callable')
            .map((entry) => entry.command),
        registryOnly,
        mixedPostureFamilies: findMixedPostureFamilies(entries),
    });
}
function findMixedPostureFamilies(entries) {
    const byParent = new Map();
    for (const entry of entries) {
        if (entry.parent === null || entry.mutationPosture === null || entry.writeCapable === null) {
            continue;
        }
        byParent.set(entry.parent, [...(byParent.get(entry.parent) ?? []), entry]);
    }
    return [...byParent.entries()]
        .map(([command, children]) => {
        const postures = [...new Set(children.map((entry) => entry.mutationPosture).filter(isPresent))].sort();
        const writeCapableValues = [...new Set(children.map((entry) => entry.writeCapable).filter(isPresent))].sort();
        return {
            command,
            postures,
            writeCapableValues,
            subcommands: children.map((entry) => entry.command).sort(compareCommandNames),
        };
    })
        .filter((entry) => entry.postures.length > 1 || entry.writeCapableValues.length > 1)
        .sort((left, right) => compareCommandNames(left.command, right.command));
}
function normalizeArguments(commandNode) {
    const registeredArguments = Array.isArray(commandNode.registeredArguments)
        ? commandNode.registeredArguments
        : [];
    return registeredArguments.map((argument) => Object.freeze({
        name: safeString(argument.name?.() ?? argument._name),
        required: Boolean(argument.required),
        variadic: Boolean(argument.variadic),
        defaultValue: argument.defaultValue,
        description: safeString(argument.description),
    }));
}
function normalizeOptions(commandNode) {
    const options = Array.isArray(commandNode.options) ? commandNode.options : [];
    return options.map((option) => Object.freeze({
        flags: safeString(option.flags),
        short: nullableString(option.short),
        long: nullableString(option.long),
        required: Boolean(option.required),
        mandatory: Boolean(option.mandatory),
        optional: Boolean(option.optional),
        variadic: Boolean(option.variadic),
        description: safeString(option.description),
        defaultValue: option.defaultValue,
    }));
}
function normalizeRuntimeSemantics(value) {
    if (typeof value !== 'object' || value === null || Array.isArray(value))
        return null;
    return value;
}
function inferSelectedArtifactRequired(args, options) {
    return (args.some((argument) => argument.required && isPathLikeName(argument.name)) ||
        options.some((option) => option.required && isPathLikeName(`${option.long ?? ''} ${option.flags}`)));
}
function isPathLikeName(value) {
    return /\b(path|artifact|definition|overview|directory|root|file|from|output|task-definition|milestone-overview)\b/i.test(value);
}
function getSubcommands(commandNode) {
    if (!Array.isArray(commandNode?.commands)) {
        return [];
    }
    return commandNode.commands.filter((entry) => typeof entry === 'object' && entry !== null);
}
function safeCommandName(commandNode) {
    const name = safeString(commandNode?.name?.());
    return name.length === 0 ? null : name;
}
function inferParentCommand(command) {
    const segments = command.trim().split(/\s+/);
    return segments.length <= 1 ? null : segments.slice(0, -1).join(' ');
}
function compareCommandNames(left, right) {
    return left.localeCompare(right, 'en');
}
function safeString(value) {
    return typeof value === 'string' ? value.trim() : '';
}
function nullableString(value) {
    const normalized = safeString(value);
    return normalized.length === 0 ? null : normalized;
}
function normalizeStringArray(value) {
    return Array.isArray(value)
        ? value
            .map((entry) => safeString(entry))
            .filter((entry) => entry.length > 0)
            .sort()
        : [];
}
function isPresent(value) {
    return value !== null;
}
function getCommandInventoryEntry(inventory, command) {
    return inventory.entries.find((entry) => entry.command === command) ?? null;
}
function getCommandInventoryCapability(command) {
    return (0, command_descriptors_1.findCommandDescriptor)(command_capabilities_1.COMMAND_DESCRIPTOR_REGISTRY, command);
}
