import { readFile } from 'node:fs/promises';
/**
 * OpenAPIConverter converts OpenAPI specifications to Agent Tool Protocol API groups.
 */
export class OpenAPIConverter {
    /**
     * Converts an OpenAPI specification to an API group configuration.
     * @param spec - OpenAPI specification object
     * @param httpClient - Function to execute HTTP requests (optional)
     * @param authEnvVarPrefix - Prefix for environment variables (e.g., 'GITHUB' -> 'GITHUB_API_KEY')
     * @returns APIGroupConfig object
     */
    static fromSpec(spec, httpClient, authEnvVarPrefix) {
        const functions = [];
        const baseUrl = spec.servers?.[0]?.url || '';
        const globalSecurity = spec.security;
        for (const [path, methods] of Object.entries(spec.paths)) {
            for (const [method, operation] of Object.entries(methods)) {
                const func = this.convertOperation(path, method, operation, httpClient, globalSecurity);
                if (func) {
                    functions.push(func);
                }
            }
        }
        const auth = this.parseAuthentication(spec, authEnvVarPrefix);
        return {
            name: this.sanitizeName(spec.info.title),
            type: 'openapi',
            spec,
            functions,
            url: baseUrl,
            auth,
        };
    }
    /**
     * Converts an OpenAPI operation to a function definition.
     */
    static convertOperation(path, method, operation, httpClient, globalSecurity) {
        const functionName = operation.operationId || this.generateFunctionName(method, path);
        const description = operation.summary || operation.description || `${method.toUpperCase()} ${path}`;
        const inputSchema = this.buildInputSchema(operation);
        const requiredScopes = this.extractRequiredScopes(operation.security || globalSecurity);
        const operationType = this.inferOperationType(method);
        const handler = httpClient
            ? async (input) => {
                return await httpClient(method, path, input);
            }
            : async (input) => {
                throw new Error(`No HTTP client configured for ${functionName}`);
            };
        return {
            name: functionName,
            description,
            inputSchema,
            handler,
            metadata: {
                operationType,
                ...(requiredScopes.length > 0 && { requiredScopes }),
            },
        };
    }
    /**
     * Builds JSON schema from OpenAPI operation parameters and request body.
     */
    static buildInputSchema(operation) {
        const properties = {};
        const required = [];
        if (operation.parameters) {
            for (const param of operation.parameters) {
                properties[param.name] = {
                    type: param.schema?.type || 'string',
                    description: param.description || param.schema?.description,
                };
                if (param.required) {
                    required.push(param.name);
                }
            }
        }
        if (operation.requestBody?.content?.['application/json']?.schema) {
            const bodySchema = operation.requestBody.content['application/json'].schema;
            if (bodySchema.properties) {
                Object.assign(properties, bodySchema.properties);
            }
            if (bodySchema.required) {
                required.push(...bodySchema.required);
            }
        }
        return {
            type: 'object',
            properties,
            ...(required.length > 0 && { required }),
        };
    }
    /**
     * Generates a function name from HTTP method and path.
     */
    static generateFunctionName(method, path) {
        const cleanPath = path
            .replace(/^[^a-zA-Z0-9_]+/, '')
            .replace(/\{([^}]+)\}/g, 'By_$1')
            .replace(/[^a-zA-Z0-9_]/g, '_')
            .replace(/_+/g, '_')
            .replace(/^_|_$/g, '');
        return `${method}_${cleanPath}`;
    }
    /**
     * Sanitizes a name for use as an API group name.
     */
    static sanitizeName(name) {
        return name
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, '_')
            .replace(/^_|_$/g, '');
    }
    /**
     * Extracts required OAuth scopes from security requirements
     */
    static extractRequiredScopes(security) {
        if (!security || security.length === 0) {
            return [];
        }
        const allScopes = new Set();
        for (const secReq of security) {
            for (const scopes of Object.values(secReq)) {
                scopes.forEach((scope) => allScopes.add(scope));
            }
        }
        return Array.from(allScopes);
    }
    /**
     * Infers operation type from HTTP method
     */
    static inferOperationType(method) {
        const m = method.toLowerCase();
        if (m === 'get' || m === 'head' || m === 'options') {
            return 'read';
        }
        if (m === 'delete') {
            return 'destructive';
        }
        return 'write';
    }
    /**
     * Parses authentication from OpenAPI security schemes
     */
    static parseAuthentication(spec, envVarPrefix) {
        if (!spec.security || spec.security.length === 0) {
            return undefined;
        }
        const securityReq = spec.security[0];
        if (!securityReq) {
            return undefined;
        }
        const schemeName = Object.keys(securityReq)[0];
        if (!schemeName || !spec.components?.securitySchemes) {
            return undefined;
        }
        const scheme = spec.components.securitySchemes[schemeName];
        if (!scheme) {
            return undefined;
        }
        const envPrefix = envVarPrefix || this.sanitizeName(spec.info.title).toUpperCase();
        switch (scheme.type) {
            case 'apiKey':
                return {
                    scheme: 'apiKey',
                    in: scheme.in === 'query' ? 'query' : 'header',
                    name: scheme.name || 'X-API-Key',
                    envVar: `${envPrefix}_API_KEY`,
                };
            case 'http':
                if (scheme.scheme === 'bearer') {
                    return {
                        scheme: 'bearer',
                        bearerFormat: scheme.bearerFormat,
                        envVar: `${envPrefix}_TOKEN`,
                    };
                }
                else if (scheme.scheme === 'basic') {
                    return {
                        scheme: 'basic',
                        usernameEnvVar: `${envPrefix}_USERNAME`,
                        passwordEnvVar: `${envPrefix}_PASSWORD`,
                    };
                }
                break;
            case 'oauth2':
                if (scheme.flows?.clientCredentials) {
                    return {
                        scheme: 'oauth2',
                        flow: 'clientCredentials',
                        tokenUrl: scheme.flows.clientCredentials.tokenUrl,
                        clientIdEnvVar: `${envPrefix}_CLIENT_ID`,
                        clientSecretEnvVar: `${envPrefix}_CLIENT_SECRET`,
                        scopes: scheme.flows.clientCredentials.scopes
                            ? Object.keys(scheme.flows.clientCredentials.scopes)
                            : undefined,
                    };
                }
                return {
                    scheme: 'bearer',
                    envVar: `${envPrefix}_TOKEN`,
                };
        }
        return undefined;
    }
    /**
     * Loads OpenAPI spec from a URL.
     * @param url - URL to fetch OpenAPI spec from
     * @param httpClient - Optional HTTP client function
     * @param authEnvVarPrefix - Prefix for environment variables
     * @returns APIGroupConfig object
     */
    static async fromURL(url, httpClient, authEnvVarPrefix) {
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(`Failed to fetch OpenAPI spec from ${url}: ${response.statusText}`);
        }
        const spec = (await response.json());
        return this.fromSpec(spec, httpClient, authEnvVarPrefix);
    }
    /**
     * Loads OpenAPI spec from a file path.
     * @param filePath - Path to OpenAPI spec file
     * @param httpClient - Optional HTTP client function
     * @param authEnvVarPrefix - Prefix for environment variables
     * @returns APIGroupConfig object
     */
    static async fromFile(filePath, httpClient, authEnvVarPrefix) {
        const content = await readFile(filePath, 'utf-8');
        const spec = JSON.parse(content);
        return this.fromSpec(spec, httpClient, authEnvVarPrefix);
    }
}
//# sourceMappingURL=index.js.map