// engine/schema-generator.ts — Validation Schema Generator (SPEC-365)
// ---------------------------------------------------------------------------
// Entity detection
// ---------------------------------------------------------------------------
const ENTITY_KEYWORDS = ['entity', 'model', 'schema', 'interface', 'type', 'object'];
const ENTITY_NAME_RE = new RegExp(`(?:${ENTITY_KEYWORDS.join('|')})\\s+([A-Z][a-zA-Z0-9]+)`, 'gi');
const FIELD_RE = /([a-z][a-zA-Z0-9]+)\s*(?::|is|as)\s*(string|number|boolean|date|uuid|email|url|int|float|array|object)\b/gi;
const OPTIONAL_RE = /optional|nullable|\?/i;
export function detectEntities(text) {
    const entityNames = new Set();
    let match;
    ENTITY_NAME_RE.lastIndex = 0;
    while ((match = ENTITY_NAME_RE.exec(text)) !== null) {
        const name = match[1];
        if (name !== undefined) {
            entityNames.add(name);
        }
    }
    return Array.from(entityNames).map((name) => ({
        name,
        fields: detectFields(text, name),
    }));
}
function detectFields(text, _entityName) {
    const fields = [];
    const seen = new Set();
    FIELD_RE.lastIndex = 0;
    let m;
    while ((m = FIELD_RE.exec(text)) !== null) {
        const fieldName = m[1];
        const rawType = m[2];
        if (fieldName === undefined || rawType === undefined) {
            continue;
        }
        if (seen.has(fieldName)) {
            continue;
        }
        seen.add(fieldName);
        const type = normalizeType(rawType);
        const surrounding = text.substring(Math.max(0, m.index - 40), m.index + 40);
        const optional = OPTIONAL_RE.test(surrounding);
        const validations = buildValidations(rawType, fieldName);
        fields.push({ name: fieldName, type, optional, validations });
    }
    return fields;
}
function normalizeType(raw) {
    const r = raw.toLowerCase();
    if (r === 'string' || r === 'email' || r === 'url' || r === 'uuid') {
        return 'string';
    }
    if (r === 'number' || r === 'int' || r === 'float') {
        return 'number';
    }
    if (r === 'boolean') {
        return 'boolean';
    }
    if (r === 'date') {
        return 'date';
    }
    return 'unknown';
}
function buildValidations(raw, fieldName) {
    const validations = [];
    const r = raw.toLowerCase();
    if (r === 'uuid') {
        validations.push('uuid');
    }
    if (r === 'email') {
        validations.push('email');
    }
    if (r === 'url') {
        validations.push('url');
    }
    if (fieldName.toLowerCase().includes('id')) {
        validations.push('min(1)');
    }
    return validations;
}
// ---------------------------------------------------------------------------
// Code generators
// ---------------------------------------------------------------------------
function zodTypeFor(field) {
    if (field.type === 'unknown') {
        return 'z.unknown() /* TODO: verify type */';
    }
    let base;
    switch (field.type) {
        case 'string':
            base = 'z.string()';
            if (field.validations.includes('uuid')) {
                base += '.uuid()';
            }
            if (field.validations.includes('email')) {
                base += '.email()';
            }
            if (field.validations.includes('url')) {
                base += '.url()';
            }
            if (field.validations.includes('min(1)')) {
                base += '.min(1)';
            }
            break;
        case 'number':
            base = 'z.number()';
            break;
        case 'boolean':
            base = 'z.boolean()';
            break;
        case 'date':
            base = 'z.string().datetime()';
            break;
        default:
            base = 'z.unknown()';
    }
    if (field.optional) {
        base += '.optional()';
    }
    return base;
}
function yupTypeFor(field) {
    if (field.type === 'unknown') {
        return 'yup.mixed() /* TODO: verify type */';
    }
    let base;
    switch (field.type) {
        case 'string':
            base = 'yup.string()';
            if (field.validations.includes('email')) {
                base += '.email()';
            }
            if (field.validations.includes('url')) {
                base += '.url()';
            }
            if (!field.optional) {
                base += '.required()';
            }
            break;
        case 'number':
            base = field.optional ? 'yup.number()' : 'yup.number().required()';
            break;
        case 'boolean':
            base = 'yup.boolean()';
            break;
        case 'date':
            base = 'yup.date()';
            break;
        default:
            base = 'yup.mixed()';
    }
    return base;
}
function valibotTypeFor(field) {
    if (field.type === 'unknown') {
        return 'v.unknown() /* TODO: verify type */';
    }
    let base;
    switch (field.type) {
        case 'string':
            base = 'v.string()';
            if (field.validations.includes('email')) {
                base = 'v.pipe(v.string(), v.email())';
            }
            if (field.validations.includes('uuid')) {
                base = 'v.pipe(v.string(), v.uuid())';
            }
            break;
        case 'number':
            base = 'v.number()';
            break;
        case 'boolean':
            base = 'v.boolean()';
            break;
        case 'date':
            base = 'v.date()';
            break;
        default:
            base = 'v.unknown()';
    }
    if (field.optional) {
        base = `v.optional(${base})`;
    }
    return base;
}
function generateZodSchema(entities, format) {
    const importLine = format === 'typescript' ? "import { z } from 'zod';" : "const { z } = require('zod');";
    const schemas = entities
        .map((entity) => {
        const fields = entity.fields.map((f) => `  ${f.name}: ${zodTypeFor(f)},`).join('\n');
        return `export const ${entity.name}Schema = z.object({\n${fields}\n});`;
    })
        .join('\n\n');
    return `${importLine}\n\n${schemas}\n`;
}
function generateYupSchema(entities, format) {
    const importLine = format === 'typescript' ? "import * as yup from 'yup';" : "const yup = require('yup');";
    const schemas = entities
        .map((entity) => {
        const fields = entity.fields.map((f) => `  ${f.name}: ${yupTypeFor(f)},`).join('\n');
        return `export const ${entity.name}Schema = yup.object({\n${fields}\n});`;
    })
        .join('\n\n');
    return `${importLine}\n\n${schemas}\n`;
}
function generateValibotSchema(entities, format) {
    const importLine = format === 'typescript' ? "import * as v from 'valibot';" : "const v = require('valibot');";
    const schemas = entities
        .map((entity) => {
        const fields = entity.fields.map((f) => `  ${f.name}: ${valibotTypeFor(f)},`).join('\n');
        return `export const ${entity.name}Schema = v.object({\n${fields}\n});`;
    })
        .join('\n\n');
    return `${importLine}\n\n${schemas}\n`;
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
export function generateSchemas(specId, specText, library, outputFormat, outputPath) {
    const entities = detectEntities(specText);
    const schemas = [];
    const libs = library === 'all' ? ['zod', 'yup', 'valibot'] : [library];
    for (const lib of libs) {
        let code;
        switch (lib) {
            case 'zod':
                code = generateZodSchema(entities, outputFormat);
                break;
            case 'yup':
                code = generateYupSchema(entities, outputFormat);
                break;
            case 'valibot':
                code = generateValibotSchema(entities, outputFormat);
                break;
        }
        schemas.push({ library: lib, code, entities: entities.map((e) => e.name) });
    }
    return {
        specId,
        library,
        outputFormat,
        schemas,
        entitiesDetected: entities.length,
        ...(outputPath !== undefined ? { outputPath } : {}),
    };
}
// ---------------------------------------------------------------------------
// Route detection for API client
// ---------------------------------------------------------------------------
const ROUTE_RE = /\b(GET|POST|PUT|PATCH|DELETE)\s+(\/api\/[^\s,)]+)/gi;
export function detectRoutes(text) {
    const routes = [];
    ROUTE_RE.lastIndex = 0;
    let m;
    while ((m = ROUTE_RE.exec(text)) !== null) {
        const rawMethod = m[1];
        const path = m[2];
        if (rawMethod === undefined || path === undefined) {
            continue;
        }
        const method = rawMethod.toUpperCase();
        routes.push({ method, path, description: `${method} ${path}` });
    }
    return routes;
}
function routeToFunctionName(method, path) {
    const segments = path.replace('/api/', '').replace(/[/:]/g, '_').replace(/_+$/, '');
    return `${method.toLowerCase()}${segments
        .split('_')
        .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
        .join('')}`;
}
function generateFetchClient(routes) {
    const functions = routes
        .map((r) => {
        const fnName = routeToFunctionName(r.method, r.path);
        const hasBody = ['POST', 'PUT', 'PATCH'].includes(r.method);
        const bodyParam = hasBody ? 'body: unknown, ' : '';
        const bodyOpts = hasBody
            ? `\n    body: JSON.stringify(body),\n    headers: { 'Content-Type': 'application/json' },`
            : '';
        return (`export async function ${fnName}(${bodyParam}options?: RequestInit): Promise<Response> {\n` +
            `  return fetch('${r.path}', { method: '${r.method}',${bodyOpts} ...options });\n` +
            `}`);
    })
        .join('\n\n');
    return `// Auto-generated fetch client\n\n${functions}\n`;
}
function generateTanstackClient(routes) {
    const queries = routes
        .map((r) => {
        const fnName = routeToFunctionName(r.method, r.path);
        const hasBody = ['POST', 'PUT', 'PATCH'].includes(r.method);
        if (r.method === 'GET') {
            return (`export function use${fnName.charAt(0).toUpperCase() + fnName.slice(1)}() {\n` +
                `  return useQuery({ queryKey: ['${r.path}'], queryFn: () => fetch('${r.path}').then(r => r.json()) });\n` +
                `}`);
        }
        const bodyParam = hasBody ? 'body: unknown' : '';
        return (`export function use${fnName.charAt(0).toUpperCase() + fnName.slice(1)}() {\n` +
            `  return useMutation({ mutationFn: (${bodyParam}) => fetch('${r.path}', {\n` +
            `    method: '${r.method}',\n` +
            `    body: JSON.stringify(body),\n` +
            `    headers: { 'Content-Type': 'application/json' },\n` +
            `  }).then(r => r.json()) });\n` +
            `}`);
    })
        .join('\n\n');
    return `// Auto-generated TanStack Query client\nimport { useQuery, useMutation } from '@tanstack/react-query';\n\n${queries}\n`;
}
export function generateApiClient(specId, specText, framework, outputPath) {
    const routes = detectRoutes(specText);
    const code = framework === 'fetch' ? generateFetchClient(routes) : generateTanstackClient(routes);
    return {
        specId,
        framework,
        code,
        routesDetected: routes.length,
        ...(outputPath !== undefined ? { outputPath } : {}),
    };
}
//# sourceMappingURL=schema-generator.js.map