"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MIGRATED_INPUT_COMMANDS = void 0;
exports.createRuntimeCommandContracts = createRuntimeCommandContracts;
exports.validateCommandDescriptorDrift = validateCommandDescriptorDrift;
const command_availability_1 = require("./command-availability");
const command_inventory_1 = require("./command-inventory");
const tool_manifest_1 = require("./tool-manifest");
const automation_protocol_1 = require("../automation-protocol");
exports.MIGRATED_INPUT_COMMANDS = Object.freeze([
    'commands search',
    'commands describe',
    'commands manifest',
    'phase review',
    'phase update',
    'milestone new',
    'milestone update',
    'task new',
    'task update',
    'implementation-summary build',
    'implementation-summary review',
    'reconciliation build',
    'reconciliation review',
    'planning inventory',
    'planning gap-report',
    'planning next-action',
    'workflow explain',
    'validation smoke',
    'validation links-check',
    'link materialize',
]);
function createRuntimeCommandContracts(program) {
    return Object.freeze((0, command_inventory_1.collectCallableCommandEvidence)(program).map((entry) => Object.freeze({
        command: entry.command,
        inputs: Object.freeze([
            ...entry.arguments.map((argument) => Object.freeze({
                kind: 'argument',
                name: argument.name,
                required: argument.required,
                variadic: argument.variadic,
                valuePosture: 'value',
            })),
            ...entry.options
                .filter((option) => option.long !== null && option.long !== '--input-json')
                .map((option) => Object.freeze({
                kind: 'option',
                name: kebabToCamel(option.long.replace(/^--/, '')),
                required: option.mandatory,
                variadic: option.variadic || Array.isArray(option.defaultValue),
                valuePosture: option.required || option.optional ? 'value' : 'flag',
            })),
        ].sort((left, right) => left.name.localeCompare(right.name))),
        semantics: entry.semantics,
    })));
}
function validateCommandDescriptorDrift(input) {
    const protocolVersion = input.protocolVersion ?? (0, automation_protocol_1.getSelectedAutomationProtocolVersion)();
    const expectedOutputSchemaRef = (0, automation_protocol_1.getAutomationProtocolEnvelopeSchemaRef)(protocolVersion);
    const migratedCommands = input.migratedCommands ??
        [
            ...new Set([
                ...exports.MIGRATED_INPUT_COMMANDS,
                ...input.runtimeCommands.filter((entry) => entry.inputs.length > 0).map((entry) => entry.command),
            ]),
        ].sort();
    const descriptorByCommand = new Map(input.descriptors.map((entry) => [entry.command, entry]));
    const runtimeByCommand = new Map(input.runtimeCommands.map((entry) => [entry.command, entry]));
    const findings = [];
    for (const descriptor of input.descriptors) {
        if (descriptor.availability === 'callable' && !runtimeByCommand.has(descriptor.command)) {
            findings.push(finding('CALLABLE_DESCRIPTOR_MISSING_RUNTIME_ACTION', descriptor.command, 'Descriptor is callable but the live Commander node has no executable action handler.'));
        }
    }
    for (const runtime of input.runtimeCommands) {
        const descriptor = descriptorByCommand.get(runtime.command);
        if (descriptor === undefined || descriptor.availability !== 'callable') {
            findings.push(finding('RUNTIME_ACTION_MISSING_CALLABLE_DESCRIPTOR', runtime.command, descriptor === undefined
                ? 'Runtime action handler has no authoritative descriptor.'
                : `Runtime action handler is paired with a non-callable '${descriptor.availability}' descriptor.`));
        }
    }
    for (const command of migratedCommands) {
        const descriptor = descriptorByCommand.get(command);
        const runtime = runtimeByCommand.get(command);
        if (descriptor === undefined) {
            findings.push(finding('MIGRATED_COMMAND_MISSING_DESCRIPTOR', command, 'Migrated command is absent from the descriptor registry.'));
            continue;
        }
        if (runtime === undefined) {
            findings.push(finding('MIGRATED_COMMAND_MISSING_RUNTIME', command, 'Migrated descriptor has no callable runtime command.'));
            continue;
        }
        const descriptorInputs = descriptor.inputs.map((entry) => entry.name).sort();
        const runtimeInputs = runtimeInputNames(runtime.inputs);
        if (!sameStrings(descriptorInputs, runtimeInputs)) {
            findings.push(finding('MIGRATED_COMMAND_INPUT_DRIFT', command, `Descriptor inputs [${descriptorInputs.join(', ')}] differ from runtime inputs [${runtimeInputs.join(', ')}].`));
        }
    }
    for (const descriptor of input.descriptors) {
        const runtime = runtimeByCommand.get(descriptor.command);
        if (runtime === undefined)
            continue;
        compareCompleteInputPosture(descriptor, runtime, findings);
        if (descriptor.semantics === null && runtime.semantics == null)
            continue;
        compareSemanticContract(descriptor, runtime, findings);
    }
    const toolNames = new Set();
    for (const tool of input.tools) {
        if (toolNames.has(tool.name)) {
            findings.push(finding('TOOL_NAME_DUPLICATE', tool.command, `Tool name '${tool.name}' is duplicated.`));
        }
        toolNames.add(tool.name);
        if (tool.outputSchema.$ref !== expectedOutputSchemaRef) {
            findings.push(finding('TOOL_OUTPUT_SCHEMA_DRIFT', tool.command, `Output schema '${tool.outputSchema.$ref}' does not match selected protocol v${protocolVersion} '${expectedOutputSchemaRef}'.`));
        }
        const descriptor = descriptorByCommand.get(tool.command);
        if (descriptor === undefined) {
            findings.push(finding('TOOL_COMMAND_MISSING_DESCRIPTOR', tool.command, 'Generated tool has no authoritative descriptor.'));
            continue;
        }
        const eligibility = (0, command_availability_1.evaluateCommandExecutableEligibility)(descriptor, runtimeByCommand.has(tool.command));
        if (!eligibility.executable) {
            findings.push(finding('TOOL_OUTSIDE_EXECUTABLE_INTERSECTION', tool.command, `Generated native tool is not eligible: ${eligibility.reason}.`));
            continue;
        }
        const expectedTool = (0, tool_manifest_1.createNativeToolDescriptor)(descriptor, protocolVersion);
        if (JSON.stringify(tool.inputSchema) !== JSON.stringify(expectedTool.inputSchema)) {
            findings.push(finding('TOOL_INPUT_SCHEMA_DRIFT', tool.command, 'Tool input schema differs from its descriptor projection.'));
        }
        if (tool.authorization !== descriptor.authorization) {
            findings.push(finding('TOOL_AUTHORIZATION_DRIFT', tool.command, 'Tool authorization differs from its descriptor.'));
        }
        if (tool.effects.filesystem !== descriptor.effects.filesystem ||
            tool.effects.network !== descriptor.effects.network ||
            tool.effects.scope !== descriptor.effects.scope) {
            findings.push(finding('TOOL_EFFECT_DRIFT', tool.command, 'Tool effects differ from its descriptor.'));
        }
    }
    findings.sort((left, right) => left.code.localeCompare(right.code) || (left.command ?? '').localeCompare(right.command ?? ''));
    const registryOnly = input.descriptors
        .filter((entry) => !runtimeByCommand.has(entry.command))
        .map((entry) => entry.command)
        .sort();
    const runtimeWithInputs = input.runtimeCommands.filter((entry) => entry.inputs.length > 0);
    const missingDescriptorInputCommands = runtimeWithInputs
        .filter((runtime) => !sameStrings(descriptorByCommand
        .get(runtime.command)
        ?.inputs.map((entry) => entry.name)
        .sort() ?? [], runtimeInputNames(runtime.inputs)))
        .map((runtime) => runtime.command)
        .sort();
    return Object.freeze({
        schemaVersion: 1,
        protocolVersion,
        verdict: findings.length === 0 ? 'valid' : 'drift',
        findings: Object.freeze(findings),
        callableCount: input.runtimeCommands.length,
        descriptorCount: input.descriptors.length,
        toolCount: input.tools.length,
        registryOnly: Object.freeze(registryOnly),
        migratedCommands: Object.freeze([...migratedCommands]),
        inputCoverage: Object.freeze({
            runtimeCommandsWithInputs: runtimeWithInputs.length,
            alignedDescriptorInputs: runtimeWithInputs.length - missingDescriptorInputCommands.length,
            missingDescriptorInputCommands: Object.freeze(missingDescriptorInputCommands),
            coverageVerdict: missingDescriptorInputCommands.length === 0 ? 'complete' : 'partial',
        }),
        readOnly: true,
    });
}
function compareCompleteInputPosture(descriptor, runtime, findings) {
    const runtimeInputs = new Map(runtime.inputs
        .filter((entry) => typeof entry !== 'string')
        .map((entry) => [entry.name, entry]));
    if (runtimeInputs.size === 0)
        return;
    for (const descriptorInput of descriptor.inputs) {
        const runtimeInput = runtimeInputs.get(descriptorInput.name);
        if (runtimeInput === undefined)
            continue;
        compareInputField('COMMAND_INPUT_KIND_DRIFT', descriptor.command, descriptorInput.name, 'kind', descriptorInput.kind, runtimeInput.kind, findings);
        compareInputField('COMMAND_INPUT_REQUIREDNESS_DRIFT', descriptor.command, descriptorInput.name, 'requiredness', descriptorInput.required, runtimeInput.required, findings);
        compareInputField('COMMAND_INPUT_VARIADIC_DRIFT', descriptor.command, descriptorInput.name, 'variadic posture', descriptorInput.variadic, runtimeInput.variadic, findings);
        compareInputField('COMMAND_INPUT_VALUE_POSTURE_DRIFT', descriptor.command, descriptorInput.name, 'value posture', descriptorValuePosture(descriptorInput), runtimeInput.valuePosture, findings);
    }
}
function compareInputField(code, command, inputName, label, descriptorValue, runtimeValue, findings) {
    if (descriptorValue === runtimeValue)
        return;
    findings.push(finding(code, command, `Input '${inputName}' descriptor ${label} '${String(descriptorValue)}' differs from runtime '${String(runtimeValue)}'.`));
}
function compareSemanticContract(descriptor, runtime, findings) {
    if (descriptor.semantics === null || runtime.semantics == null) {
        findings.push(finding('COMMAND_SEMANTIC_EVIDENCE_MISSING', descriptor.command, descriptor.semantics === null
            ? 'Runtime adapter declares semantic evidence but the descriptor does not.'
            : 'Descriptor declares semantic evidence but the runtime adapter does not.'));
        return;
    }
    compareSemanticField('COMMAND_CONDITIONAL_INPUT_DRIFT', 'conditional inputs', descriptor.command, descriptor.semantics, runtime.semantics, 'conditionalInputs', findings);
    compareSemanticField('COMMAND_DEPENDENT_INPUT_DRIFT', 'dependent inputs', descriptor.command, descriptor.semantics, runtime.semantics, 'dependentInputs', findings);
    compareSemanticField('COMMAND_CONFLICTING_INPUT_DRIFT', 'conflicting inputs', descriptor.command, descriptor.semantics, runtime.semantics, 'conflictingInputs', findings);
    compareSemanticField('COMMAND_MUTABLE_FIELD_DRIFT', 'mutable fields', descriptor.command, descriptor.semantics, runtime.semantics, 'mutableFields', findings);
    compareSemanticField('COMMAND_GENERIC_GUARD_DRIFT', 'generic guards', descriptor.command, descriptor.semantics, runtime.semantics, 'genericGuards', findings);
    compareSemanticField('COMMAND_FAILURE_TAXONOMY_DRIFT', 'failure taxonomy', descriptor.command, descriptor.semantics, runtime.semantics, 'failureTaxonomy', findings);
    compareSemanticField('COMMAND_RECOVERY_CALLABILITY_DRIFT', 'recovery callability', descriptor.command, descriptor.semantics, runtime.semantics, 'recoveries', findings);
    compareSemanticField('COMMAND_EXACT_EXAMPLE_DRIFT', 'exact example argv', descriptor.command, descriptor.semantics, runtime.semantics, 'exactExamples', findings);
}
function compareSemanticField(code, label, command, descriptor, runtime, key, findings) {
    if (JSON.stringify(descriptor[key]) === JSON.stringify(runtime[key]))
        return;
    findings.push(finding(code, command, `Descriptor ${label} differ from runtime semantic evidence.`));
}
function descriptorValuePosture(input) {
    return input.valueType === 'boolean' ? 'flag' : 'value';
}
function runtimeInputNames(inputs) {
    return inputs.map((entry) => (typeof entry === 'string' ? entry : entry.name)).sort();
}
function finding(code, command, detail) {
    return Object.freeze({ code, command, detail });
}
function sameStrings(left, right) {
    return left.length === right.length && left.every((entry, index) => entry === right[index]);
}
function kebabToCamel(value) {
    return value.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
}
