/**
 * Backend-agnostic data route factory.
 *
 * Eliminates boilerplate REST endpoints by wrapping any HazoConnectAdapter
 * into standard GET/POST/PATCH/DELETE handlers that match the request format
 * expected by client hooks (use_validation_rules, use_elements, etc.).
 *
 * Usage:
 *   import { create_data_route } from 'hazo_collab_forms/lib';
 *   import { getHazoConnect } from '@/lib/hazo_connect_setup';
 *
 *   export const { GET, POST, PATCH, DELETE } = create_data_route(getHazoConnect());
 */
import 'server-only';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function parse_filters(params) {
    const filters = [];
    params.forEach((value, key) => {
        const match = key.match(/^filter\[(.+?)\](?:\[(.+)\])?$/);
        if (!match)
            return;
        filters.push({
            column: match[1],
            operator: (match[2] ?? 'eq').toLowerCase(),
            value,
        });
    });
    return filters;
}
function parse_order_direction(dir) {
    if (!dir)
        return undefined;
    const norm = dir.toLowerCase();
    if (norm === 'asc' || norm === 'desc')
        return norm;
    throw new Error(`Unsupported order direction '${dir}'`);
}
function error_response(message, status = 500) {
    return Response.json({ error: message }, { status });
}
function validate_table(table, allowed) {
    if (!table) {
        return error_response("Query parameter 'table' is required.", 400);
    }
    if (allowed && !allowed.includes(table)) {
        return error_response(`Access to table '${table}' is not allowed.`, 403);
    }
    return table;
}
// ---------------------------------------------------------------------------
// Factory
// ---------------------------------------------------------------------------
/**
 * Create GET/POST/PATCH/DELETE route handlers backed by any HazoConnectAdapter.
 *
 * Request/response format matches the existing client-side convention:
 *
 * - GET  ?table=X&filter[col]=val&orderBy=col&orderDirection=asc → { data, total }
 * - POST { table, data }                                        → { data }
 * - PATCH { table, criteria, data }                             → { data, updated }
 * - DELETE { table, criteria }                                  → { data, deleted }
 */
export function create_data_route(adapter, options) {
    const allowed = options?.allowed_tables;
    // Lazy-load createCrudService so the hazo_connect package is optional at install time
    let _crud_factory = null;
    let _admin_service = null;
    let _resolve_promise = null;
    async function resolve_backend() {
        if (_crud_factory || _admin_service)
            return;
        if (_resolve_promise) {
            await _resolve_promise;
            return;
        }
        _resolve_promise = (async () => {
            try {
                // Prefer createCrudService (backend-agnostic)
                const mod = await import(/* webpackIgnore: true */ 'hazo_connect/server');
                if (typeof mod.createCrudService === 'function') {
                    _crud_factory = (a, t) => mod.createCrudService(a, t);
                    return;
                }
            }
            catch {
                // createCrudService not available — try admin service
            }
            try {
                const mod = await import(/* webpackIgnore: true */ 'hazo_connect/server');
                if (typeof mod.getSqliteAdminService === 'function') {
                    _admin_service = mod.getSqliteAdminService();
                    return;
                }
            }
            catch {
                // fall through
            }
            throw new Error('hazo_connect is required for create_data_route. Install it: npm install hazo_connect');
        })();
        await _resolve_promise;
    }
    // ------- GET -------
    async function GET(request) {
        const url = new URL(request.url);
        const table_or_err = validate_table(url.searchParams.get('table'), allowed);
        if (table_or_err instanceof Response)
            return table_or_err;
        const table = table_or_err;
        try {
            await resolve_backend();
            if (_crud_factory) {
                return await get_via_crud(table, url.searchParams);
            }
            if (_admin_service) {
                return await get_via_admin(table, url.searchParams);
            }
            return error_response('No backend resolved');
        }
        catch (err) {
            return error_response(err instanceof Error ? err.message : 'Failed to fetch data');
        }
    }
    async function get_via_crud(table, params) {
        const crud = _crud_factory(adapter, table);
        const filters = parse_filters(params);
        const order_by = params.get('orderBy') ?? undefined;
        const order_dir = parse_order_direction(params.get('orderDirection'));
        const limit = params.has('limit') ? Number.parseInt(params.get('limit'), 10) : undefined;
        const offset = params.has('offset') ? Number.parseInt(params.get('offset'), 10) : undefined;
        const rows = await crud.list((qb) => {
            // Apply filters using where(field, operator, value)
            for (const f of filters) {
                qb = qb.where(f.column, f.operator, f.value);
            }
            // Apply ordering using order(field, direction)
            if (order_by) {
                qb = qb.order(order_by, order_dir ?? 'asc');
            }
            // Apply pagination
            if (limit !== undefined && !Number.isNaN(limit)) {
                qb = qb.limit(limit);
            }
            if (offset !== undefined && !Number.isNaN(offset)) {
                qb = qb.offset(offset);
            }
            return qb;
        });
        return Response.json({ data: rows, total: rows.length });
    }
    async function get_via_admin(table, params) {
        const filters = parse_filters(params);
        const order_by = params.get('orderBy') ?? undefined;
        const order_dir = parse_order_direction(params.get('orderDirection'));
        const limit_str = params.get('limit');
        const offset_str = params.get('offset');
        const limit = limit_str ? Number.parseInt(limit_str, 10) : undefined;
        const offset = offset_str ? Number.parseInt(offset_str, 10) : undefined;
        const page = await _admin_service.getTableData(table, {
            limit: Number.isNaN(limit) ? undefined : limit,
            offset: Number.isNaN(offset) ? undefined : offset,
            order_by,
            order_direction: order_dir,
            filters: filters.length
                ? filters.map(f => ({ column: f.column, operator: f.operator, value: f.value }))
                : undefined,
        });
        return Response.json({ data: page.rows, total: page.total });
    }
    // ------- POST -------
    async function POST(request) {
        try {
            await resolve_backend();
            const payload = await request.json();
            const table = payload?.table;
            const data = payload?.data;
            if (!table || typeof data !== 'object' || Array.isArray(data)) {
                return error_response("Request body must include 'table' and a 'data' object.", 400);
            }
            const table_or_err = validate_table(table, allowed);
            if (table_or_err instanceof Response)
                return table_or_err;
            if (_crud_factory) {
                const crud = _crud_factory(adapter, table);
                const inserted = await crud.insert(data);
                // insert returns array; return first element
                const row = Array.isArray(inserted) ? inserted[0] : inserted;
                return Response.json({ data: row });
            }
            if (_admin_service) {
                const row = await _admin_service.insertRow(table, data);
                return Response.json({ data: row });
            }
            return error_response('No backend resolved');
        }
        catch (err) {
            return error_response(err instanceof Error ? err.message : 'Failed to insert row');
        }
    }
    // ------- PATCH -------
    async function PATCH(request) {
        try {
            await resolve_backend();
            const payload = await request.json();
            const table = payload?.table;
            const data = payload?.data;
            const criteria = payload?.criteria;
            if (!table ||
                typeof data !== 'object' || Array.isArray(data) ||
                typeof criteria !== 'object' || criteria === null || Array.isArray(criteria)) {
                return error_response("Request body must include 'table', 'data' object, and a 'criteria' object.", 400);
            }
            const table_or_err = validate_table(table, allowed);
            if (table_or_err instanceof Response)
                return table_or_err;
            if (_crud_factory) {
                const crud = _crud_factory(adapter, table);
                // Use list + filter to find matching rows, then update each
                const matching = await crud.findBy(criteria);
                const results = [];
                for (const row of matching) {
                    const id = row.id;
                    if (id !== undefined) {
                        const updated = await crud.updateById(id, data);
                        results.push(...(Array.isArray(updated) ? updated : [updated]));
                    }
                }
                return Response.json({ data: results, updated: results.length });
            }
            if (_admin_service) {
                const rows = await _admin_service.updateRows(table, criteria, data);
                return Response.json({ data: rows, updated: rows.length });
            }
            return error_response('No backend resolved');
        }
        catch (err) {
            return error_response(err instanceof Error ? err.message : 'Failed to update rows');
        }
    }
    // ------- DELETE -------
    async function DELETE(request) {
        try {
            await resolve_backend();
            const payload = await request.json();
            const table = payload?.table;
            const criteria = payload?.criteria;
            if (!table ||
                typeof criteria !== 'object' || criteria === null || Array.isArray(criteria)) {
                return error_response("Request body must include 'table' and a 'criteria' object.", 400);
            }
            const table_or_err = validate_table(table, allowed);
            if (table_or_err instanceof Response)
                return table_or_err;
            if (_crud_factory) {
                const crud = _crud_factory(adapter, table);
                const matching = await crud.findBy(criteria);
                const deleted = [];
                for (const row of matching) {
                    const id = row.id;
                    if (id !== undefined) {
                        await crud.deleteById(id);
                        deleted.push(row);
                    }
                }
                return Response.json({ data: deleted, deleted: deleted.length });
            }
            if (_admin_service) {
                const rows = await _admin_service.deleteRows(table, criteria);
                return Response.json({ data: rows, deleted: rows.length });
            }
            return error_response('No backend resolved');
        }
        catch (err) {
            return error_response(err instanceof Error ? err.message : 'Failed to delete rows');
        }
    }
    return { GET, POST, PATCH, DELETE };
}
//# sourceMappingURL=create_data_route.js.map