import { anthropicShellInvocationConstructionPractice } from "./anthropic.js";
import { deepmindShellInvocationConstructionPractice } from "./deepmind.js";
import { openaiShellInvocationConstructionPractice } from "./openai.js";
import { buildShellPracticeAuditMetadata, createShellBaseToolDefinition, injectRuntimeInvocationMetadata, jsonSchema, selectShellProviderPractice, } from "../../_shared/baseToolAdapter.js";
import { evaluateShellGenerationProviderDispatch, isShellGenerationRecord, shellGenerationDryRunEnabled, } from "../_shared/providerDispatch.js";
import { requireProviderAudit, requireProviderEvents, requireProviderFailureError, requireProviderResultEnvelope, requireRecordField, } from "../_shared/hostExecutorProvider.js";
import { constructShellInvocation, } from "./core.js";
import { normalizeShellInvocationEnvelope, shellInvocationConstructionDependencyDeclarations, } from "./dependencies.js";
export const shellInvocationConstructionProviderPractices = [
    anthropicShellInvocationConstructionPractice,
    openaiShellInvocationConstructionPractice,
    deepmindShellInvocationConstructionPractice,
];
const praxisNativeShellInvocationConstructionPractice = {
    providerName: "praxis-native",
    source: {
        kind: "praxis-native",
        label: "Praxis deterministic shell invocation construction core",
    },
    directCliSupport: false,
    sideEffectPolicy: "runtime-governed",
    notes: ["Used for metadata only when no provider-specific practice is selected; dry-run still uses the deterministic core directly."],
    createProvider: ({ provider }) => provider,
};
export const shellInvocationConstructionBestPracticeDescriptor = {
    toolId: "shell.invocationConstruction",
    bestPractice: "runtime-governed-shell-invocation-construction",
    sourcePriority: ["cli", "agent-sdk", "api-sdk", "praxis-native"],
    providerOrder: ["anthropic", "openai", "deepmind"],
    dependencies: shellInvocationConstructionDependencyDeclarations,
};
export function selectShellInvocationConstructionPractice(dependencies = {}) {
    return selectShellProviderPractice(shellInvocationConstructionProviderPractices, dependencies, praxisNativeShellInvocationConstructionPractice);
}
function buildShellInvocationConstructionPracticeAuditMetadata(selection) {
    return buildShellPracticeAuditMetadata({
        providerName: selection.providerName,
        sourceLabel: selection.practice.source.label,
        sourceKind: selection.practice.source.kind,
        sourcePath: selection.practice.source.path,
        directCliSupport: selection.practice.directCliSupport,
        sideEffectPolicy: selection.practice.sideEffectPolicy,
        notes: selection.practice.notes,
    });
}
function invocationConstructionProviderFailure(code, message) {
    return {
        ok: false,
        toolId: "shell.invocationConstruction",
        error: {
            code,
            message,
            boundary: code === "GOVERNANCE_REJECTED" ? "governance" : "contract",
            publicSafe: true,
            internalDetailExposed: false,
        },
        audit: [],
        events: ["basicTool.shell.invocationConstruction.rejected"],
    };
}
const shellInvocationConstructionProviderErrorCodes = [
    "MISSING_COMMAND",
    "MISSING_GUARD",
    "INVALID_COMMAND",
    "INVALID_GUARD",
    "GUARD_BLOCKED",
    "PERMISSION_DENIED",
    "GOVERNANCE_REJECTED",
    "PROVIDER_UNAVAILABLE",
    "PROVIDER_REJECTED",
    "REAL_EXECUTION_BLOCKED",
];
const shellInvocationConstructionProviderErrorBoundaries = ["input", "permission", "contract", "governance"];
function normalizeShellInvocationConstructionProviderResult(result) {
    const envelope = requireProviderResultEnvelope(result, "shell.invocationConstruction");
    const audit = requireProviderAudit(envelope);
    const events = requireProviderEvents(envelope);
    if (!envelope.ok) {
        return {
            ok: false,
            toolId: "shell.invocationConstruction",
            error: requireProviderFailureError(envelope, shellInvocationConstructionProviderErrorCodes, shellInvocationConstructionProviderErrorBoundaries),
            audit,
            events,
        };
    }
    return {
        ok: true,
        toolId: "shell.invocationConstruction",
        invocation: normalizeShellInvocationEnvelope(requireRecordField(envelope, "invocation")),
        audit,
        events,
    };
}
export async function constructShellInvocationBestPractice(request = {}) {
    if (!isShellGenerationRecord(request) || shellGenerationDryRunEnabled(request.context)) {
        return constructShellInvocation(request);
    }
    const dispatch = evaluateShellGenerationProviderDispatch(request.context);
    if (!dispatch.ok) {
        return invocationConstructionProviderFailure(dispatch.code, dispatch.message);
    }
    const validation = constructShellInvocation({
        ...request,
        context: {
            ...request.context,
            dryRun: true,
        },
    });
    if (!validation.ok) {
        return validation;
    }
    const selection = selectShellInvocationConstructionPractice({
        executor: request.executor,
        provider: request.provider,
        preferredProvider: request.preferredProvider,
    });
    if (selection.provider === undefined) {
        return invocationConstructionProviderFailure("PROVIDER_UNAVAILABLE", "shell.invocationConstruction provider-backed generation requires an injected runtime provider");
    }
    try {
        const providerResult = await selection.provider({
            ...request,
            context: {
                ...request.context,
                auditMetadata: {
                    ...(request.context?.auditMetadata ?? {}),
                    ...buildShellInvocationConstructionPracticeAuditMetadata(selection),
                },
            },
        });
        return normalizeShellInvocationConstructionProviderResult(providerResult);
    }
    catch {
        return invocationConstructionProviderFailure("PROVIDER_REJECTED", "shell.invocationConstruction provider rejected the generation request");
    }
}
export const shellInvocationConstructionBaseToolDefinition = createShellBaseToolDefinition({
    toolId: "shell.invocationConstruction",
    title: "Shell Invocation Construction",
    description: "Build a runtime-visible shell invocation envelope from generated command and guard material.",
    summary: "Use shell.invocationConstruction after command generation and execution guard evaluation.",
    storageGroup: "shellGeneration",
    riskLevel: "normal",
    permissionHints: ["shell:generate"],
    dependencies: shellInvocationConstructionDependencyDeclarations,
    inputSchema: jsonSchema("shell.invocationConstruction.input", { type: "object", additionalProperties: true }),
    outputSchema: jsonSchema("shell.invocationConstruction.output", { type: "object", additionalProperties: true }),
});
function adaptResult(result) {
    if (!result.ok) {
        return {
            ok: false,
            toolId: result.toolId,
            error: { code: result.error.code, message: result.error.message, publicSafe: true },
            events: result.events,
        };
    }
    return { ok: true, toolId: result.toolId, output: result.invocation, events: result.events, metadata: { audit: result.audit } };
}
export const shellInvocationConstructionHandler = {
    definition: shellInvocationConstructionBaseToolDefinition,
    async invoke(request) {
        const input = isShellGenerationRecord(request.input) ? request.input : {};
        const inputContext = isShellGenerationRecord(input.context) ? input.context : {};
        const auditMetadata = isShellGenerationRecord(inputContext.auditMetadata) ? inputContext.auditMetadata : undefined;
        const selection = selectShellInvocationConstructionPractice({
            preferredProvider: input.preferredProvider,
        });
        return adaptResult(await constructShellInvocationBestPractice({
            ...input,
            executor: request.executor,
            context: {
                ...inputContext,
                runtimeId: typeof inputContext.runtimeId === "string" ? inputContext.runtimeId : request.runtimeId,
                sessionId: typeof inputContext.sessionId === "string" ? inputContext.sessionId : request.sessionId,
                invocationId: typeof inputContext.invocationId === "string" ? inputContext.invocationId : request.toolCallId,
                auditMetadata: injectRuntimeInvocationMetadata({
                    ...buildShellInvocationConstructionPracticeAuditMetadata(selection),
                    ...(request.metadata ?? {}),
                }, auditMetadata, request),
            },
        }));
    },
};
