// engine/schema-parity-auditor/index.ts — Orchestrator for SPEC-449
import { readFile, writeFile } from 'node:fs/promises';
import { glob } from 'glob';
import { scanZodOptionalFields } from './zod-scanner.js';
import { checkTypeOptional } from './type-checker.js';
import { findHandlerFile, checkHandlerAccess } from './handler-checker.js';
/**
 * Default type values to use when auto-fixing handlers.
 * Maps Zod base type → default value string.
 */
const ZOD_TYPE_DEFAULTS = {
    'z.string': "''",
    'z.array': '[]',
    'z.number': '0',
    'z.boolean': 'false',
    'z.object': '{}',
};
/**
 * Audits all register-*.ts files in the project for Zod/TypeScript optional parity issues.
 * Returns a report with all discrepancies found.
 */
export async function auditSchemaParity(projectPath) {
    const optionalFields = await scanZodOptionalFields(projectPath);
    const discrepancies = await buildDiscrepancies(optionalFields, projectPath);
    const criticalCount = discrepancies.filter((d) => d.severity === 'critical').length;
    const warningCount = discrepancies.filter((d) => d.severity === 'warning').length;
    const summary = discrepancies.length === 0
        ? `All ${String(optionalFields.length)} optional fields have correct TypeScript types and safe handler access.`
        : `Found ${String(discrepancies.length)} discrepancies (${String(criticalCount)} critical, ${String(warningCount)} warnings) across ${String(optionalFields.length)} optional fields.`;
    return { totalOptionalFields: optionalFields.length, discrepancies, summary };
}
async function buildDiscrepancies(fields, projectPath) {
    const results = [];
    for (const field of fields) {
        const typeResult = await checkTypeOptional(field.fieldName, projectPath);
        // Only report discrepancies where we found the field in a type but without "?"
        if (!typeResult.found || typeResult.hasOptionalMark) {
            continue;
        }
        const handlerFile = await findHandlerFile(field.registerFile, projectPath);
        let hasUnsafeAccess = false;
        if (handlerFile !== undefined) {
            const handlerResult = await checkHandlerAccess(field.fieldName, handlerFile);
            hasUnsafeAccess = handlerResult.hasUnsafeAccess;
        }
        const severity = hasUnsafeAccess ? 'critical' : 'warning';
        const defaultValue = ZOD_TYPE_DEFAULTS[field.zodType] ?? 'undefined';
        const suggestedFix = buildSuggestedFix(field.fieldName, typeResult.typeFile, handlerFile, defaultValue, hasUnsafeAccess);
        results.push({
            registerFile: field.registerFile,
            fieldName: field.fieldName,
            toolName: field.toolName,
            typeFile: typeResult.typeFile,
            hasMissingOptionalInType: true,
            hasUnsafeAccess,
            severity,
            suggestedFix,
        });
    }
    return results;
}
function buildSuggestedFix(fieldName, typeFile, handlerFile, defaultValue, hasUnsafeAccess) {
    const fixes = [`In ${typeFile}: change \`${fieldName}:\` to \`${fieldName}?:\``];
    if (hasUnsafeAccess && handlerFile !== undefined) {
        fixes.push(`In ${handlerFile}: add fallback \`${fieldName} ?? ${defaultValue}\` before accessing \`${fieldName}.xxx\``);
    }
    return fixes.join('; ');
}
/**
 * Applies automatic fixes for Zod/TypeScript parity issues.
 * When dryRun=true, returns previews without writing files.
 */
export async function fixSchemaParity(projectPath, dryRun) {
    const audit = await auditSchemaParity(projectPath);
    const fixes = [];
    for (const discrepancy of audit.discrepancies) {
        if (discrepancy.hasMissingOptionalInType) {
            const fix = await fixTypeFile(discrepancy.fieldName, discrepancy.typeFile, dryRun);
            if (fix !== null) {
                fixes.push(fix);
            }
        }
    }
    const summary = dryRun
        ? `Dry run: ${String(fixes.length)} fixes would be applied.`
        : `Applied ${String(fixes.length)} fixes successfully.`;
    return { dryRun, fixes, summary };
}
async function fixTypeFile(fieldName, typeFile, dryRun) {
    const typeFiles = await glob(typeFile);
    if (typeFiles.length === 0) {
        return null;
    }
    const resolvedFile = typeFiles[0];
    if (resolvedFile === undefined) {
        return null;
    }
    const content = await readFile(resolvedFile, 'utf-8');
    // Replace `fieldName:` with `fieldName?:` (only when not already optional)
    const fieldRe = new RegExp(`(\\b${escapeRegex(fieldName)})(\\s*:)(?!\\s*\\?)`, 'g');
    const fixed = content.replace(fieldRe, '$1?$2');
    if (fixed === content) {
        return null; // No change needed
    }
    const description = `Added "?" to field "${fieldName}" in ${resolvedFile}`;
    if (!dryRun) {
        await writeFile(resolvedFile, fixed, 'utf-8');
    }
    return {
        file: resolvedFile,
        description,
        preview: dryRun ? `- ${fieldName}: → ${fieldName}?:` : undefined,
    };
}
function escapeRegex(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
//# sourceMappingURL=index.js.map