import { BestAgentError, isRecord } from "@best-agent/foundation";
import { isDeepEqual, isPlainObject } from "./builtins/shared.js";
import { inferToolEffectFromExecutionClass } from "./effect.js";
import { isZodToolInputSchema, parseZodToolInput } from "./schema.js";
export function toRuntimeToolDefinition(tool) {
    const summarize = tool.summarize;
    return {
        approvalClass: tool.approvalClass,
        capabilities(input, context) {
            return tool.capabilities(input, context);
        },
        concurrencyGroup: tool.concurrencyGroup,
        description: tool.description,
        effect: tool.effect ?? inferToolEffectFromExecutionClass(tool.executionClass),
        evidenceExtractor: tool.evidenceExtractor
            ? (input, output, envelope) => tool.evidenceExtractor?.(input, output, envelope) ?? []
            : undefined,
        execute(input, context) {
            return tool.execute(input, context);
        },
        executionClass: tool.executionClass,
        expectedDurationClass: tool.expectedDurationClass,
        formatInput(input) {
            return tool.formatInput?.(input) ?? formatToolInput(input);
        },
        heartbeatPolicy: tool.heartbeatPolicy,
        historyInput: tool.historyInput ? (input) => tool.historyInput?.(input) : undefined,
        idempotent: tool.idempotent,
        inputSchema: tool.inputSchema,
        isContentBearingInput: tool.isContentBearingInput
            ? (input) => tool.isContentBearingInput?.(input) ?? false
            : undefined,
        name: tool.name,
        async parse(input, context) {
            return toToolInputValue(await parseToolInputForDefinition(tool, input, context));
        },
        providerHistoryInput: tool.providerHistoryInput
            ? (input) => tool.providerHistoryInput?.(input) ?? input
            : undefined,
        replayPolicy: tool.replayPolicy,
        resultEnvelope: tool.resultEnvelope
            ? (input, output) => tool.resultEnvelope?.(input, output) ?? {}
            : undefined,
        source: tool.source,
        sourceExtension: tool.sourceExtension,
        sourcePlugin: tool.sourcePlugin,
        sourceServer: tool.sourceServer,
        summarize: summarize ? (input, output) => summarize(input, output) : undefined,
    };
}
export async function parseToolInputForDefinition(tool, input, context) {
    const toolInputMode = context.toolInputMode ?? "strict";
    if (toolInputMode === "legacy" && tool.legacyAdapter) {
        const preparedInput = await tool.legacyAdapter.adapt(input, context);
        if (!isZodToolInputSchema(tool.inputSchema)) {
            return preparedInput;
        }
        return parseToolInput(tool.name, tool.inputSchema, preparedInput);
    }
    return parseToolInput(tool.name, tool.inputSchema, input);
}
export function parseToolInputForDefinitionSync(tool, input) {
    if (isZodToolInputSchema(tool.inputSchema)) {
        return parseToolInput(tool.name, tool.inputSchema, input);
    }
    return parseToolInput(tool.name, tool.inputSchema, input);
}
export function parseRuntimeToolInput(toolName, inputSchema, input) {
    return toToolInputValue(parseToolInput(toolName, inputSchema, input));
}
function parseToolInput(toolName, inputSchema, input) {
    if (isZodToolInputSchema(inputSchema)) {
        return parseZodToolInput(toolName, inputSchema, input);
    }
    if (!isSchemaObject(inputSchema)) {
        return input;
    }
    validateAgainstSchema(toolName, inputSchema, input, "input");
    return input;
}
function validateAgainstSchema(toolName, schema, value, path) {
    if (schema.enum && !schema.enum.some((candidate) => isDeepEqual(candidate, value))) {
        throw new BestAgentError("TOOL_INPUT_INVALID", `${toolName} ${path} must match one of the allowed values.`);
    }
    if (schema.type) {
        const types = Array.isArray(schema.type) ? schema.type : [schema.type];
        if (!types.some((type) => valueMatchesType(value, type))) {
            throw new BestAgentError("TOOL_INPUT_INVALID", `${toolName} ${path} does not match the expected schema.`);
        }
    }
    if (schema.type === "object") {
        if (!isPlainObject(value)) {
            return;
        }
        const properties = schema.properties ?? {};
        const required = new Set(schema.required ?? []);
        for (const key of required) {
            if (!(key in value)) {
                throw new BestAgentError("TOOL_INPUT_INVALID", `${toolName} ${path}.${key} is required.`);
            }
        }
        for (const [key, candidate] of Object.entries(value)) {
            const childSchema = properties[key];
            if (!childSchema) {
                if (schema.additionalProperties === false) {
                    throw new BestAgentError("TOOL_INPUT_INVALID", `${toolName} ${path}.${key} is not allowed.`);
                }
                continue;
            }
            validateAgainstSchema(toolName, childSchema, candidate, `${path}.${key}`);
        }
    }
    if (schema.type === "array" && Array.isArray(value) && schema.items) {
        value.forEach((item, index) => validateAgainstSchema(toolName, schema.items, item, `${path}[${index}]`));
    }
}
function valueMatchesType(value, type) {
    switch (type) {
        case "array":
            return Array.isArray(value);
        case "boolean":
            return typeof value === "boolean";
        case "integer":
            return typeof value === "number" && Number.isInteger(value);
        case "number":
            return typeof value === "number";
        case "object":
            return isPlainObject(value);
        case "string":
            return typeof value === "string";
    }
    return false;
}
function isSchemaObject(value) {
    return isRecord(value);
}
function toToolInputValue(input) {
    return input;
}
function formatToolInput(input) {
    if (typeof input === "string") {
        return input;
    }
    return JSON.stringify(input);
}
//# sourceMappingURL=definition-adapter.js.map