"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());
}
