import yaml from "js-yaml";
import { MCP_TOOL_TO_OPERATION_ID, OPENAPI_OPERATION_MAPPINGS } from "./contract_mappings.js";
import { readOpenApiFile } from "./openapi_file.js";
let cachedSpec = null;
let cachedOperations = null;
function loadOpenApiSpec() {
    if (cachedSpec) {
        return cachedSpec;
    }
    const raw = readOpenApiFile();
    cachedSpec = yaml.load(raw);
    return cachedSpec;
}
function getOperationIndex() {
    if (cachedOperations) {
        return cachedOperations;
    }
    const spec = loadOpenApiSpec();
    const operations = new Map();
    const paths = spec.paths ?? {};
    for (const methods of Object.values(paths)) {
        for (const operation of Object.values(methods)) {
            if (operation?.operationId) {
                operations.set(operation.operationId, operation);
            }
        }
    }
    cachedOperations = operations;
    return operations;
}
function resolveSchema(schema, spec) {
    const ref = schema.$ref;
    if (typeof ref === "string" && ref.startsWith("#/components/schemas/")) {
        const schemaName = ref.replace("#/components/schemas/", "");
        const resolved = spec.components?.schemas?.[schemaName];
        if (!resolved) {
            throw new Error(`OpenAPI schema not found: ${schemaName}`);
        }
        return resolveSchema(resolved, spec);
    }
    if (Array.isArray(schema.allOf)) {
        const merged = { type: "object", properties: {}, required: [] };
        for (const entry of schema.allOf) {
            const resolved = resolveSchema(entry, spec);
            if (resolved.type === "object") {
                const properties = (resolved.properties ?? {});
                const required = (resolved.required ?? []);
                merged.properties = { ...merged.properties, ...properties };
                merged.required = Array.from(new Set([...merged.required, ...required]));
            }
        }
        return merged;
    }
    if (schema.type === "array" && schema.items) {
        return { ...schema, items: resolveSchema(schema.items, spec) };
    }
    if (schema.type === "object" && schema.properties) {
        const resolvedProps = {};
        for (const [key, value] of Object.entries(schema.properties)) {
            resolvedProps[key] = resolveSchema(value, spec);
        }
        return { ...schema, properties: resolvedProps };
    }
    return schema;
}
function buildParametersSchema(parameters, spec) {
    const schema = { type: "object", properties: {}, required: [] };
    if (!parameters) {
        return schema;
    }
    for (const param of parameters) {
        if (!param?.name || !param.schema) {
            continue;
        }
        const properties = schema.properties;
        properties[param.name] = resolveSchema(param.schema, spec);
        if (param.required) {
            schema.required.push(param.name);
        }
    }
    return schema;
}
function mergeSchemas(base, extra) {
    const baseProps = (base.properties ?? {});
    const extraProps = (extra.properties ?? {});
    const baseRequired = (base.required ?? []);
    const extraRequired = (extra.required ?? []);
    return {
        type: "object",
        properties: { ...baseProps, ...extraProps },
        required: Array.from(new Set([...baseRequired, ...extraRequired])),
    };
}
export function getOpenApiInputSchemaForOperationId(operationId) {
    const spec = loadOpenApiSpec();
    const operation = getOperationIndex().get(operationId);
    if (!operation) {
        throw new Error(`OpenAPI operation not found: ${operationId}`);
    }
    const parametersSchema = buildParametersSchema(operation.parameters, spec);
    const jsonSchema = operation.requestBody?.content?.["application/json"]?.schema;
    if (jsonSchema) {
        const resolvedBody = resolveSchema(jsonSchema, spec);
        if (resolvedBody.type === "object") {
            return mergeSchemas(parametersSchema, resolvedBody);
        }
        return resolvedBody;
    }
    return parametersSchema;
}
export function getOpenApiInputSchemaForTool(toolName) {
    const operationId = MCP_TOOL_TO_OPERATION_ID[toolName];
    if (!operationId) {
        return null;
    }
    return getOpenApiInputSchemaForOperationId(operationId);
}
export function getOpenApiInputSchemaOrThrow(toolName) {
    const schema = getOpenApiInputSchemaForTool(toolName);
    if (!schema) {
        throw new Error(`OpenAPI schema not mapped for MCP tool: ${toolName}`);
    }
    return schema;
}
export function listOpenApiMappedMcpTools() {
    return OPENAPI_OPERATION_MAPPINGS.flatMap((mapping) => mapping.mcpTool ? [mapping.mcpTool] : []);
}
//# sourceMappingURL=openapi_schema.js.map