/**
 * OpenAPI Transformer
 * 
 * Ported from Hoppscotch's openapi/index.ts
 * Converts OpenAPI Document to BrainfishCollection
 */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { makeBrainfishCollection, makeBrainfishRESTRequest, makeBrainfishRESTResponseOriginalRequest } from '../../factories';
import { objectHasProperty, cloneDeep } from '../../utils';
import { hasUnresolvedRefs } from './dereferencer';
import { replaceOpenApiPathTemplating, parseOpenAPIParams, parseOpenAPIHeaders, parseOpenAPIVariables, parseOpenAPIResponses } from './extractors';
import { parseOpenAPIBody } from './extractors/body';
import { parseOpenAPIAuth, extractSecurityHeaders, getDefaultAuth } from './extractors/auth';
/**
 * Formats an operation name for display
 * Converts snake_case/kebab-case operationIds to readable Title Case
 * Prefers summary over operationId if available
 */ function formatOperationName(info) {
    const summary = info.summary?.trim();
    const operationId = info.operationId?.trim();
    // Prefer summary as it's usually more human-readable
    if (summary) {
        return summary;
    }
    // If only operationId is available, convert to readable format
    if (operationId) {
        return operationId// Replace underscores and hyphens with spaces
        .replace(/[_-]/g, ' ')// Handle camelCase by adding space before capital letters
        .replace(/([a-z])([A-Z])/g, '$1 $2')// Capitalize first letter of each word
        .replace(/\b\w/g, (char)=>char.toUpperCase())// Clean up multiple spaces
        .replace(/\s+/g, ' ').trim();
    }
    return 'Untitled Request';
}
/**
 * Parses OpenAPI URL from document (v2 or v3)
 */ function parseOpenAPIUrl(doc) {
    // OpenAPI V2 has host and basePath in document properties
    if (objectHasProperty(doc, 'swagger')) {
        const host = doc.host?.trim() || '<<baseUrl>>';
        const basePath = doc.basePath?.trim() || '';
        return `${host}${basePath}`;
    }
    // OpenAPI V3 has servers array
    if (objectHasProperty(doc, 'servers')) {
        const serverUrl = doc.servers?.[0]?.url;
        return !serverUrl || serverUrl === './' ? '<<baseUrl>>' : serverUrl;
    }
    // Fallback
    return '<<baseUrl>>';
}
/**
 * Converts an OpenAPI path to Brainfish requests
 */ function convertPathToBrainfishReqs(doc, pathName, pathObj) {
    const methods = [
        'get',
        'head',
        'post',
        'put',
        'delete',
        'options',
        'patch'
    ];
    return methods.filter((method)=>!!pathObj[method]).map((method)=>{
        const info = pathObj[method];
        const openAPIUrl = parseOpenAPIUrl(doc);
        const openAPIPath = replaceOpenApiPathTemplating(pathName);
        const endpoint = openAPIUrl.endsWith('/') && openAPIPath.startsWith('/') ? openAPIUrl + openAPIPath.slice(1) : openAPIUrl + openAPIPath;
        const params = parseOpenAPIParams(info.parameters ?? []);
        const baseHeaders = parseOpenAPIHeaders(info.parameters ?? []);
        // Get the primary auth key (handled by global auth) to exclude from additional headers
        const defaultAuth = getDefaultAuth(doc);
        const primaryAuthKey = defaultAuth?.authType === 'api-key' ? defaultAuth.key : undefined;
        // Extract additional security headers (e.g., agent-key when access-token is primary)
        const securityHeaders = extractSecurityHeaders(doc, info, primaryAuthKey);
        // Merge headers: base headers + security headers (avoiding duplicates)
        const existingKeys = new Set(baseHeaders.map((h)=>h.key.toLowerCase()));
        const additionalSecurityHeaders = securityHeaders.filter((h)=>!existingKeys.has(h.key.toLowerCase())).map((h)=>({
                key: h.key,
                value: h.value,
                active: h.active,
                description: h.description
            }));
        const headers = [
            ...baseHeaders,
            ...additionalSecurityHeaders
        ];
        const requestVariables = parseOpenAPIVariables(info.parameters ?? []);
        const auth = parseOpenAPIAuth(doc, info);
        const body = parseOpenAPIBody(doc, info);
        const displayName = formatOperationName(info);
        const originalRequest = makeBrainfishRESTResponseOriginalRequest({
            name: displayName,
            method: method.toUpperCase(),
            endpoint,
            params,
            headers,
            body,
            auth,
            requestVariables
        });
        const responses = parseOpenAPIResponses(doc, info, originalRequest);
        const request = makeBrainfishRESTRequest({
            name: displayName,
            description: info.description ?? null,
            method: method.toUpperCase(),
            endpoint,
            params,
            headers,
            auth,
            body,
            requestVariables,
            responses,
            tags: info.tags ?? [],
            operationId: info.operationId ?? undefined
        });
        return {
            request,
            metadata: {
                tags: info.tags ?? []
            }
        };
    });
}
/**
 * Converts OpenAPI documents to BrainfishCollections
 */ export function convertOpenAPIToCollection(docs) {
    // Check for unresolved references before conversion
    for (const doc of docs){
        if (hasUnresolvedRefs(doc)) {
            console.warn('Document contains unresolved references which may affect import quality');
        // Continue anyway to provide a best-effort import
        }
    }
    const collections = docs.map((doc)=>{
        const name = doc.info.title;
        const description = doc.info.description ?? null;
        // Extract tag descriptions from OpenAPI spec
        const tagDescriptions = {};
        if ('tags' in doc && Array.isArray(doc.tags)) {
            doc.tags.forEach((tag)=>{
                if (tag.name && tag.description) {
                    tagDescriptions[tag.name] = tag.description;
                }
            });
        }
        // Convert all paths to requests
        const paths = Object.entries(doc.paths ?? {}).map(([pathName, pathObj])=>convertPathToBrainfishReqs(doc, pathName, pathObj)).flat();
        // Organize requests by tags
        const requestsByTags = {};
        const requestsWithoutTags = [];
        paths.forEach(({ metadata, request })=>{
            const tags = metadata.tags;
            if (tags.length === 0) {
                requestsWithoutTags.push(request);
                return;
            }
            for (const tag of tags){
                if (!requestsByTags[tag]) {
                    requestsByTags[tag] = [];
                }
                // Clone request for each tag (request can belong to multiple tags)
                requestsByTags[tag].push(cloneDeep(request));
            }
        });
        // Create folders (collections) for each tag
        const folders = Object.entries(requestsByTags).map(([tagName, requests])=>makeBrainfishCollection({
                name: tagName,
                description: tagDescriptions[tagName] ?? null,
                requests,
                folders: [],
                variables: [],
                auth: {
                    authType: 'inherit',
                    authActive: true
                },
                headers: []
            }));
        // Create main collection
        return makeBrainfishCollection({
            name,
            description,
            folders,
            requests: requestsWithoutTags,
            variables: [],
            auth: {
                authType: 'inherit',
                authActive: true
            },
            headers: []
        });
    });
    return collections;
}
