/**
 * OpenAPI Body Extractors
 * 
 * Ported from Hoppscotch's openapi/index.ts
 * Handles request body parsing for v2 and v3
 */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { knownContentTypes, isOpenAPIV3Operation } from './index';
/**
 * Resolve a $ref reference in the schema
 */ function resolveRef(doc, ref) {
    if (!ref.startsWith('#/')) return null;
    try {
        const path = ref.slice(2).split('/');
        let current = doc;
        for (const segment of path){
            if (current && typeof current === 'object' && segment in current) {
                current = current[segment];
            } else {
                return null;
            }
        }
        return current;
    } catch  {
        return null;
    }
}
/**
 * Generate a sample value based on schema type
 */ function generateSampleValue(schema, doc, required = false) {
    try {
        // Handle $ref
        if ('$ref' in schema) {
            const resolved = resolveRef(doc, schema.$ref);
            if (resolved) {
                return generateSampleValue(resolved, doc, required);
            }
            return null;
        }
        const schemaObj = schema;
        // Use example if provided
        if (schemaObj.example !== undefined) {
            return schemaObj.example;
        }
        // Use default if provided
        if (schemaObj.default !== undefined) {
            return schemaObj.default;
        }
        // Use enum first value if provided
        if (schemaObj.enum && schemaObj.enum.length > 0) {
            return schemaObj.enum[0];
        }
        // Generate based on type
        switch(schemaObj.type){
            case 'string':
                if (schemaObj.format === 'date') return '2024-01-01';
                if (schemaObj.format === 'date-time') return '2024-01-01T00:00:00Z';
                if (schemaObj.format === 'email') return 'user@example.com';
                if (schemaObj.format === 'uri' || schemaObj.format === 'url') return 'https://example.com';
                if (schemaObj.format === 'uuid') return '00000000-0000-0000-0000-000000000000';
                return required ? 'string' : '';
            case 'number':
            case 'integer':
                if (schemaObj.minimum !== undefined) return schemaObj.minimum;
                return 0;
            case 'boolean':
                return false;
            case 'array':
                if (schemaObj.items) {
                    return [
                        generateSampleValue(schemaObj.items, doc, true)
                    ];
                }
                return [];
            case 'object':
                return generateObjectFromSchema(schemaObj, doc);
            default:
                // If no type but has properties, treat as object
                if (schemaObj.properties) {
                    return generateObjectFromSchema(schemaObj, doc);
                }
                return null;
        }
    } catch  {
        return null;
    }
}
/**
 * Generate a sample object from an OpenAPI schema
 * Required fields are listed first
 */ function generateObjectFromSchema(schema, doc) {
    try {
        const result = {};
        const requiredFields = new Set(schema.required || []);
        const properties = schema.properties || {};
        // Sort properties to put required fields first
        const sortedEntries = Object.entries(properties).sort(([keyA], [keyB])=>{
            const aRequired = requiredFields.has(keyA);
            const bRequired = requiredFields.has(keyB);
            if (aRequired && !bRequired) return -1;
            if (!aRequired && bRequired) return 1;
            return 0;
        });
        for (const [key, propSchema] of sortedEntries){
            const isRequired = requiredFields.has(key);
            const value = generateSampleValue(propSchema, doc, isRequired);
            if (value !== null) {
                result[key] = value;
            }
        }
        return result;
    } catch  {
        return {};
    }
}
/**
 * Generate example from OpenAPI v3 media object
 */ function generateExampleFromMedia(media, doc) {
    try {
        // First check for explicit examples
        if (media.example !== undefined) {
            return media.example;
        }
        if (media.examples && Object.keys(media.examples).length > 0) {
            const firstExampleKey = Object.keys(media.examples)[0];
            const firstExample = media.examples[firstExampleKey];
            if ('value' in firstExample) {
                return firstExample.value;
            }
        }
        // Generate from schema
        const schema = media.schema;
        if (!schema) return null;
        // Handle $ref at schema level
        if ('$ref' in schema) {
            const resolved = resolveRef(doc, schema.$ref);
            if (resolved) {
                if (resolved.type === 'object' || resolved.properties) {
                    return generateObjectFromSchema(resolved, doc);
                }
                return generateSampleValue(resolved, doc, true);
            }
            return null;
        }
        if (schema.type === 'object' || schema.properties) {
            return generateObjectFromSchema(schema, doc);
        }
        return generateSampleValue(schema, doc, true);
    } catch  {
        return null;
    }
}
// Legacy function for v2 compatibility
function generateRequestBodyExampleFromOpenAPIV2Body(op, doc) {
    try {
        const bodyParam = (op.parameters ?? []).find((param)=>param.in === 'body');
        if (!bodyParam?.schema) return null;
        const schema = bodyParam.schema;
        if (schema.type === 'object' || schema.properties) {
            const example = generateObjectFromSchema(schema, doc);
            return JSON.stringify(example, null, 2);
        }
        return null;
    } catch  {
        return null;
    }
}
function generateV3Example(media, doc) {
    return generateExampleFromMedia(media, doc);
}
function generateV31Example(media, doc) {
    return generateExampleFromMedia(media, doc);
}
/**
 * Parses OpenAPI v2 request body
 */ export function parseOpenAPIV2Body(op, doc) {
    const obj = (op.consumes ?? [])[0];
    // Not a content-type we support
    if (!obj || !(obj in knownContentTypes)) {
        return {
            contentType: null,
            body: null
        };
    }
    // For form data types, extract form fields
    if (obj === 'multipart/form-data' || obj === 'application/x-www-form-urlencoded') {
        const formDataValues = (op.parameters ?? []).filter((param)=>param.in === 'formData').map((param)=>({
                key: param.name,
                isFile: param.type === 'file',
                value: '',
                active: true
            }));
        return obj === 'application/x-www-form-urlencoded' ? {
            contentType: obj,
            body: formDataValues.map(({ key })=>`${key}: `).join('\n')
        } : {
            contentType: obj,
            body: formDataValues
        };
    }
    // For other content types (JSON, XML, etc.)
    const bodyParam = (op.parameters ?? []).find((param)=>param.in === 'body');
    if (bodyParam && doc) {
        const result = generateRequestBodyExampleFromOpenAPIV2Body(op, doc);
        if (result) {
            return {
                contentType: obj,
                body: result
            };
        }
    }
    // Fallback to empty body for textual content types
    return {
        contentType: obj,
        body: ''
    };
}
/**
 * Parses OpenAPI v3 form data body
 */ export function parseOpenAPIV3BodyFormData(contentType, mediaObj) {
    const schema = mediaObj.schema;
    if (!schema || schema.type !== 'object') {
        return contentType === 'application/x-www-form-urlencoded' ? {
            contentType,
            body: ''
        } : {
            contentType,
            body: []
        };
    }
    const keys = Object.keys(schema.properties ?? {});
    if (contentType === 'application/x-www-form-urlencoded') {
        return {
            contentType,
            body: keys.map((key)=>`${key}: `).join('\n')
        };
    }
    return {
        contentType,
        body: keys.map((key)=>({
                key,
                value: '',
                isFile: false,
                active: true
            }))
    };
}
/**
 * Parses OpenAPI v3 request body
 */ export function parseOpenAPIV3Body(doc, op) {
    const objs = Object.entries(op.requestBody?.content ?? {});
    if (objs.length === 0) return {
        contentType: null,
        body: null
    };
    // We only take the first definition
    const [contentType, media] = objs[0];
    if (!(contentType in knownContentTypes)) {
        return {
            contentType: null,
            body: null
        };
    }
    // Handle form data types
    if (contentType === 'multipart/form-data' || contentType === 'application/x-www-form-urlencoded') {
        return parseOpenAPIV3BodyFormData(contentType, media);
    }
    // For other content types (JSON, XML, etc.), try to generate sample from schema
    try {
        const docAny = doc;
        const isV31 = docAny.openapi && docAny.openapi.startsWith('3.1');
        let sampleBody;
        if (isV31) {
            sampleBody = generateV31Example(media, doc);
        } else {
            sampleBody = generateV3Example(media, doc);
        }
        if (sampleBody !== null && sampleBody !== undefined) {
            return {
                contentType: contentType,
                body: typeof sampleBody === 'string' ? sampleBody : JSON.stringify(sampleBody, null, 2)
            };
        }
    } catch  {
    // Fall through to empty body
    }
    // Fallback to empty body for textual content types
    return {
        contentType: contentType,
        body: ''
    };
}
/**
 * Parses OpenAPI request body (v2 or v3)
 */ export function parseOpenAPIBody(doc, op) {
    return isOpenAPIV3Operation(doc, op) ? parseOpenAPIV3Body(doc, op) : parseOpenAPIV2Body(op, doc);
}
