/**
 * App backend types and factory — database initialization + auth migrations + deps.
 *
 * Provides `AppBackend`, `CreateAppBackendOptions`, and `create_app_backend()`.
 *
 * **Vocabulary**:
 * - `AppDeps` — stateless capabilities: injectable, swappable per environment
 * - `*Options` — static values set at startup, per-factory configuration
 * - Runtime state — mutable values (e.g., `bootstrap_status`) — NOT in deps or options
 *
 * @module
 */
import { Logger } from '@fuzdev/fuz_util/log.js';
import { create_audit_emitter } from '../auth/audit_emitter.js';
import { run_migrations } from '../db/migrate.js';
import { auth_migration_ns, reserved_migration_namespaces } from '../auth/migrations.js';
import { create_db } from '../db/create_db.js';
/**
 * Trivial `AuditFactory` for consumers that don't compose `on_audit_event`
 * or `audit_log_config`. Equivalent to
 * `({db, log}) => create_audit_emitter({db, log})` — exported so the
 * default case stays a single-symbol reference rather than five tokens
 * of boilerplate at every consumer.
 *
 * Use the inline form when you need to thread `on_audit_event` /
 * `audit_log_config` / `emit_decorator`; the factory composes those
 * three fields itself so there's nothing this constant can pass through.
 */
export const default_audit_factory = ({ db, log }) => create_audit_emitter({ db, log });
/**
 * Initialize the backend: database + auth migrations + deps.
 *
 * Calls `create_db` → `run_migrations` (auth namespace, then any
 * `migration_namespaces` from options in order) → `audit_factory({db, log})`
 * and bundles the result with the provided keyring and password deps.
 *
 * @param options - keyring, password deps, `audit_factory`, optional database URL, and optional `migration_namespaces`
 * @returns app backend with deps, database metadata, and combined migration results
 * @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
 */
export const create_app_backend = async (options) => {
    const { database_url, keyring, password, stat, read_text_file, delete_file, audit_factory } = options;
    const log = options.log ?? new Logger('server');
    const { db, close, db_type, db_name } = await create_db(database_url);
    // Everything after `create_db` can throw — reserved-namespace check,
    // `run_migrations` (seven MigrationError kinds), `audit_factory`.
    // Without this guard the pool leaks because `close` is only returned
    // on the success path. Cleanup errors are logged and swallowed so the
    // caller sees the original failure, not a teardown-shaped one.
    try {
        if (options.migration_namespaces?.length) {
            for (const ns of options.migration_namespaces) {
                if (reserved_migration_namespaces.includes(ns.namespace)) {
                    throw new Error(`Migration namespace "${ns.namespace}" is reserved by fuz_app — choose a different namespace`);
                }
            }
        }
        const migration_results = await run_migrations(db, [
            auth_migration_ns,
            ...(options.migration_namespaces ?? []),
        ]);
        const audit = audit_factory({ db, log });
        return {
            db_type,
            db_name,
            migration_results,
            close,
            deps: {
                keyring,
                password,
                db,
                stat,
                read_text_file,
                delete_file,
                log,
                audit,
            },
        };
    }
    catch (err) {
        try {
            await close();
        }
        catch (close_err) {
            log.error('create_app_backend: failed to close db after init error:', close_err);
        }
        throw err;
    }
};
