import { guardAccepted, isJsonObject, optionalTrimmedString } from "../../_shared/baseToolAdapter.js";
import { createMcpToolRegistryAuditEvent, createMcpToolRegistryFailure, ensureMcpToolRegistryPermissions, ensureMcpToolRegistryScope, mcpRegisterToolDescriptor, normalizeMcpServerId, normalizeMcpToolName, policyFailure, } from "../mcp.registerTool/core.js";
export const mcpUpdateToolDescriptor = {
    toolId: "mcp.updateTool",
    capability: "update-mcp-tool-definition",
    route: "agent_executionEngine.basic_toolLayer.baseTools.mcpBase.tool",
    permissionsRequired: mcpRegisterToolDescriptor.permissionsRequired,
    defaultDryRun: true,
    unsafeSideEffects: true,
    tapOwnsApproval: true,
    runtimeOwnsMcpClient: true,
    providerBoundary: "BaseToolExecutorPort.mcp.updateTool",
};
function normalizeContext(value) {
    if (value === undefined)
        return {};
    if (!isJsonObject(value))
        return { ok: false, code: "INVALID_CONTEXT", message: "mcp.updateTool context must be a JSON object.", boundary: "input" };
    const base = value;
    return {
        ...base,
        serverId: optionalTrimmedString(base.serverId),
        invocationId: optionalTrimmedString(base.invocationId),
        runtimeId: optionalTrimmedString(base.runtimeId),
        sessionId: optionalTrimmedString(base.sessionId),
    };
}
function normalizeUpdatePatch(patch, context, serverId) {
    if (!isJsonObject(patch) || Object.keys(patch).length === 0) {
        return createMcpToolRegistryFailure("mcp.updateTool", "MISSING_UPDATE_PATCH", "mcp.updateTool requires a non-empty target.patch", "input", context, serverId);
    }
    if (patch.inputSchema !== undefined && !isJsonObject(patch.inputSchema)) {
        return createMcpToolRegistryFailure("mcp.updateTool", "INVALID_UPDATE_PATCH", "mcp.updateTool target.patch.inputSchema must be an object when provided", "contract", context, serverId);
    }
    if (patch.outputSchema !== undefined && !isJsonObject(patch.outputSchema)) {
        return createMcpToolRegistryFailure("mcp.updateTool", "INVALID_UPDATE_PATCH", "mcp.updateTool target.patch.outputSchema must be an object when provided", "contract", context, serverId);
    }
    if (patch.metadata !== undefined && !isJsonObject(patch.metadata)) {
        return createMcpToolRegistryFailure("mcp.updateTool", "INVALID_UPDATE_PATCH", "mcp.updateTool target.patch.metadata must be an object when provided", "contract", context, serverId);
    }
    const normalized = {};
    const name = optionalTrimmedString(patch.name);
    const description = optionalTrimmedString(patch.description);
    if (name !== undefined)
        normalized.name = name;
    if (description !== undefined)
        normalized.description = description;
    if (isJsonObject(patch.inputSchema))
        normalized.inputSchema = patch.inputSchema;
    if (isJsonObject(patch.outputSchema))
        normalized.outputSchema = patch.outputSchema;
    if (isJsonObject(patch.metadata))
        normalized.metadata = patch.metadata;
    if (Object.keys(normalized).length === 0) {
        return createMcpToolRegistryFailure("mcp.updateTool", "MISSING_UPDATE_PATCH", "mcp.updateTool requires a non-empty target.patch", "input", context, serverId);
    }
    return normalized;
}
function validate(request) {
    const root = request === undefined ? {} : request;
    if (!isJsonObject(root))
        return { ok: false, code: "INVALID_REQUEST", message: "mcp.updateTool request must be a JSON object.", boundary: "input" };
    const context = normalizeContext(root.context);
    if ("ok" in context)
        return context;
    if (root.target !== undefined && root.target !== null && !isJsonObject(root.target))
        return { ok: false, code: "MISSING_SERVER_ID", message: "mcp.updateTool requires target.serverId or context.serverId", boundary: "input", context };
    const target = isJsonObject(root.target) ? root.target : {};
    const serverId = normalizeMcpServerId("mcp.updateTool", target.serverId, context);
    if (typeof serverId !== "string")
        return serverId;
    const toolName = normalizeMcpToolName("mcp.updateTool", target.toolName, context, serverId);
    if (typeof toolName !== "string")
        return toolName;
    const patch = normalizeUpdatePatch(target.patch, context, serverId);
    if ("ok" in patch)
        return patch;
    const scope = ensureMcpToolRegistryScope("mcp.updateTool", serverId, context);
    if (scope !== undefined)
        return scope;
    const permissions = ensureMcpToolRegistryPermissions("mcp.updateTool", mcpUpdateToolDescriptor.permissionsRequired, context, serverId);
    if (permissions !== undefined)
        return permissions;
    return { ok: true, target: { serverId, toolName, patch }, context, acceptedScopes: context.requestedScopes ?? [] };
}
function auditEvent(type, dryRun, context, serverId, metadata) {
    return createMcpToolRegistryAuditEvent("mcp.updateTool", type, { ...context, dryRun }, serverId, metadata);
}
function envelope(target, result) {
    return {
        toolName: result?.toolName ?? target.toolName,
        state: result?.status ?? "planned",
        patchKeys: Object.keys(target.patch).sort(),
        source: result === undefined ? "mockable-envelope" : "runtime-provider",
    };
}
function output(normalized, options) {
    return {
        kind: "agentCore.basicTool.mcp.updateTool",
        target: normalized.target,
        dryRun: options.dryRun,
        executionBlocked: options.dryRun,
        providerCalled: options.providerCalled,
        permissionsRequired: mcpUpdateToolDescriptor.permissionsRequired,
        unsafeSideEffects: true,
        acceptedScopes: normalized.acceptedScopes,
        registryEnvelope: envelope(normalized.target, options.result),
        providerMetadata: options.result?.providerMetadata,
    };
}
export function planMcpToolUpdate(request = {}) {
    const normalized = validate(request);
    if (!normalized.ok) {
        if ("error" in normalized)
            return normalized;
        return createMcpToolRegistryFailure("mcp.updateTool", normalized.code, normalized.message, normalized.boundary, normalized.context);
    }
    const blocked = policyFailure("mcp.updateTool", normalized.context, normalized.target.serverId);
    if (blocked !== undefined)
        return blocked;
    if (normalized.context.dryRun === false)
        return createMcpToolRegistryFailure("mcp.updateTool", "REAL_EXECUTION_BLOCKED", "mcp.updateTool only returns a guarded dry-run plan through plan* helpers", "contract", normalized.context, normalized.target.serverId);
    return { ok: true, toolId: "mcp.updateTool", output: output(normalized, { dryRun: true, providerCalled: false }), audit: [auditEvent("agentCore.basicTool.mcp.updateTool.dryRun", true, normalized.context, normalized.target.serverId, { toolName: normalized.target.toolName, patchKeys: Object.keys(normalized.target.patch).sort() })], events: ["basicTool.mcp.updateTool.dryRun"] };
}
export async function executeMcpToolUpdate(request = {}, provider) {
    const normalized = validate(request);
    if (!normalized.ok) {
        if ("error" in normalized)
            return normalized;
        return createMcpToolRegistryFailure("mcp.updateTool", normalized.code, normalized.message, normalized.boundary, normalized.context);
    }
    const blocked = policyFailure("mcp.updateTool", normalized.context, normalized.target.serverId);
    if (blocked !== undefined)
        return blocked;
    if (normalized.context.dryRun !== false)
        return { ok: true, toolId: "mcp.updateTool", output: output(normalized, { dryRun: true, providerCalled: false }), audit: [auditEvent("agentCore.basicTool.mcp.updateTool.dryRun", true, normalized.context, normalized.target.serverId, { toolName: normalized.target.toolName, patchKeys: Object.keys(normalized.target.patch).sort() })], events: ["basicTool.mcp.updateTool.dryRun"] };
    if (!guardAccepted(normalized.context.guard))
        return createMcpToolRegistryFailure("mcp.updateTool", "GOVERNANCE_REJECTED", "mcp.updateTool requires an accepted runtime guard before real dispatch.", "governance", normalized.context, normalized.target.serverId);
    if (provider === undefined)
        return createMcpToolRegistryFailure("mcp.updateTool", "PROVIDER_UNAVAILABLE", "Runtime MCP updateTool provider is unavailable.", "provider", normalized.context, normalized.target.serverId, "basicTool.mcp.updateTool.providerUnavailable");
    try {
        const result = await provider(normalized.target, { runtimeId: normalized.context.runtimeId, sessionId: normalized.context.sessionId, invocationId: normalized.context.invocationId, auditMetadata: normalized.context.auditMetadata });
        return { ok: true, toolId: "mcp.updateTool", output: output(normalized, { dryRun: false, providerCalled: true, result }), audit: [auditEvent("agentCore.basicTool.mcp.updateTool.executed", false, normalized.context, normalized.target.serverId, { toolName: normalized.target.toolName })], events: ["basicTool.mcp.updateTool.executed"] };
    }
    catch {
        return createMcpToolRegistryFailure("mcp.updateTool", "PROVIDER_REJECTED", "Runtime MCP provider failed updateTool.", "provider", normalized.context, normalized.target.serverId, "basicTool.mcp.updateTool.providerRejected");
    }
}
