/**
 * Server-side autofill route handler factory.
 *
 * Routes LLM extraction through hazo_files so results are persisted
 * in the hazo_files table (file_data column).
 *
 *   import { create_autofill_route } from 'hazo_collab_forms/lib';
 *   export const POST = create_autofill_route({ extraction_service, ... });
 *
 * Uses standard Web API Request/Response for portability (works in Next.js,
 * Deno, Cloudflare Workers, etc.).
 */
import 'server-only';
// ============================================================================
// Helpers
// ============================================================================
/**
 * Build a detailed field list string from field schemas.
 * Used as the {{field_list}} variable in LLM prompts.
 */
function build_field_list_string(fields) {
    return fields.map((f) => {
        let desc = `- "${f.field_id}" (${f.label})`;
        if (f.description)
            desc += `: ${f.description}`;
        if (f.input_type === 'numeric')
            desc += ' [numeric value]';
        if (f.input_options?.length) {
            desc += ` [one of: ${f.input_options.map((o) => o.value).join(', ')}]`;
        }
        if (f.component_type === 'HazoCollabFormCheckbox')
            desc += ' [true/false]';
        if (f.component_type === 'HazoCollabFormDate')
            desc += ' [date in YYYY-MM-DD format]';
        // Data table: enumerate columns so the LLM knows exact column IDs/types
        if (f.component_type === 'HazoCollabFormDataTable' && f.table_config) {
            const config = f.table_config;
            if (config.columns?.length) {
                desc += ': array of row objects with columns:';
                for (const col of config.columns) {
                    let col_desc = `\n    - "${col.id}" (${col.label})`;
                    if (col.field_type === 'numeric' || col.field_type === 'currency') {
                        col_desc += ': numeric';
                        if (col.constraints?.decimal_places !== undefined) {
                            col_desc += ` [${col.constraints.decimal_places} decimal places]`;
                        }
                    }
                    else if (col.field_type === 'dropdown' || col.field_type === 'radiobutton') {
                        col_desc += `: ${col.field_type}`;
                    }
                    else {
                        col_desc += ': text';
                    }
                    if (col.constraints?.required)
                        col_desc += ' [required]';
                    desc += col_desc;
                }
            }
            else {
                desc += ' [array of row objects]';
            }
        }
        return desc;
    }).join('\n');
}
// ============================================================================
// Route Handler Factory
// ============================================================================
/**
 * Creates a POST route handler for autofill document extraction.
 *
 * The handler:
 * 1. Validates request (prompt_area, prompt_key required)
 * 2. Resolves file content (inline base64, file_resolver, or download_url)
 * 3. Calls extraction_service.extract() for prompt-based LLM extraction
 * 4. Falls back to a generated default prompt if first attempt fails
 * 5. Persists extraction results to hazo_files via metadata_service
 * 6. Returns extracted data as JSON
 */
export function create_autofill_route(options) {
    const { extraction_service, metadata_service } = options;
    const file_resolver = options.file_resolver;
    const on_error = options.on_error ?? ((err, ctx) => console.error(`[autofill] ${ctx}:`, err));
    return async function POST(request) {
        try {
            const body = await request.json();
            const { file_name, mime_type, download_url, file_b64: request_file_b64, prompt_area, prompt_key, fields, } = body;
            // Validate required fields
            if (!prompt_area || !prompt_key) {
                return Response.json({ success: false, error: 'prompt_area and prompt_key are required', data: {} }, { status: 400 });
            }
            if (!fields?.length) {
                return Response.json({ success: false, error: 'fields array is required and must not be empty', data: {} }, { status: 400 });
            }
            // Get file as base64 — priority: inline data > file_resolver > download_url
            let file_b64;
            if (request_file_b64) {
                file_b64 = request_file_b64;
            }
            else if (file_resolver && body.file_id) {
                try {
                    file_b64 = await file_resolver(body.file_id);
                }
                catch (err) {
                    return Response.json({ success: false, error: `Failed to resolve file "${file_name}" (${body.file_id}): ${err instanceof Error ? err.message : 'Unknown error'}`, data: {} }, { status: 500 });
                }
            }
            else {
                try {
                    // Skip blob: URLs — they are browser-only and can't be fetched server-side
                    if (download_url.startsWith('blob:')) {
                        throw new Error('blob: URLs are not accessible server-side. Configure file_resolver on the autofill route or send file_b64.');
                    }
                    const absolute_url = download_url.startsWith('http')
                        ? download_url
                        : `${new URL(request.url).origin}${download_url}`;
                    const file_response = await fetch(absolute_url);
                    if (!file_response.ok) {
                        throw new Error(`Failed to fetch file: ${file_response.status}`);
                    }
                    const buffer = Buffer.from(await file_response.arrayBuffer());
                    file_b64 = buffer.toString('base64');
                }
                catch (err) {
                    return Response.json({ success: false, error: `Failed to fetch uploaded file "${file_name}": ${err instanceof Error ? err.message : 'Unknown error'}`, data: {} }, { status: 500 });
                }
            }
            // Build field_list string for prompt variable substitution
            const field_list = build_field_list_string(fields);
            const buffer = Buffer.from(file_b64, 'base64');
            // Prompt-based extraction. The prompt MUST exist in hazo_prompts —
            // there is no hardcoded fallback. If extraction fails or returns empty,
            // surface that to the caller rather than silently retrying with a
            // generated prompt (which would mask missing/broken DB prompts).
            const result = await extraction_service.extract(buffer, mime_type, {
                promptArea: prompt_area,
                promptKey: prompt_key,
                promptVariables: { field_list },
            });
            // Persist extraction to hazo_files
            if (result.success && result.data && metadata_service && body.file_id) {
                try {
                    const record = await metadata_service.findById(body.file_id);
                    if (record) {
                        await metadata_service.addExtraction(record.file_path, record.storage_type, result.data, { source: 'autofill' });
                    }
                }
                catch (err) {
                    on_error(err, 'failed to persist extraction to hazo_files');
                    // Non-fatal: extraction data is still returned to client
                }
            }
            // Strip internal debug field from data before returning
            const clean_data = { ...(result.data ?? {}) };
            delete clean_data['_resolved_prompt'];
            const has_data = Object.entries(clean_data).some(([, v]) => {
                if (v === null || v === undefined || v === '')
                    return false;
                if (Array.isArray(v) && v.length === 0)
                    return false;
                return true;
            });
            // Resolve the full prompt text for debugging
            let resolved_prompt = result.data?.['_resolved_prompt'];
            if (!resolved_prompt && options.prompt_resolver) {
                try {
                    resolved_prompt = await options.prompt_resolver(prompt_area, prompt_key, { field_list }) ?? undefined;
                }
                catch { /* non-fatal */ }
            }
            return Response.json({
                success: result.success,
                data: clean_data,
                ...(result.error ? { error: result.error } : {}),
                ...(!has_data ? { message: 'No matching data found in this document.' } : {}),
                _debug: {
                    prompt_area,
                    prompt_key,
                    resolved_prompt,
                },
            });
        }
        catch (error) {
            on_error(error, 'unhandled error');
            return Response.json({ success: false, error: error instanceof Error ? error.message : 'Unknown error', data: {} }, { status: 500 });
        }
    };
}
//# sourceMappingURL=autofill_handler.js.map