import '../assert_dev_env.js';
import { Logger } from '@fuzdev/fuz_util/log.js';
import { write_daemon_info, read_daemon_info, is_daemon_running } from '../../cli/daemon.js';
/** Hosts that expose the test binary beyond loopback — refused at startup. */
const OPEN_HOSTS = new Set(['0.0.0.0', '::', '[::]']);
/**
 * Boot a test-mode server using the supplied runtime adapter.
 *
 * Mirrors a production `start_server` at the surface level — stale-daemon
 * check, daemon-info write, bind, graceful drain — but the app is the
 * caller's no-domain (or domain) {@link StartTestingServerOptions.build_app}
 * and the runtime boundary is the {@link TestingServerAdapter}. Refuses to
 * bind an open host (the test binary must stay on loopback).
 */
export const start_testing_server = async (options) => {
    const { adapter, daemon_name, host, port, app_version, build_app } = options;
    const log = options.log ?? new Logger(`[${daemon_name}]`);
    const { runtime } = adapter;
    if (OPEN_HOSTS.has(host)) {
        log.error(`FATAL: binding to '${host}' exposes the test binary to your entire network. ` +
            `Use --host localhost (default) or 127.0.0.1 instead.`);
        adapter.exit(1);
    }
    const stale = await read_daemon_info(runtime, daemon_name);
    if (stale) {
        if (await is_daemon_running(runtime, stale.pid)) {
            log.warn('found running server', stale);
        }
        else {
            log.warn(`stale daemon.json (pid ${stale.pid} not running), replacing`);
        }
    }
    const built = await build_app();
    let ws;
    if (built.mount_websocket) {
        ws = adapter.prepare_websocket(built.app);
        built.mount_websocket(ws.upgrade_websocket);
    }
    await write_daemon_info(runtime, daemon_name, {
        version: 1,
        pid: adapter.pid,
        port,
        started: new Date().toISOString(),
        app_version: app_version ?? '0.0.0-test',
    });
    log.info(`Listening on http://${host}:${port} (${adapter.runtime_label}, test mode)`);
    const server = adapter.serve({ fetch: built.app.fetch, port, hostname: host });
    ws?.attach_to_server?.(server);
    let shutting_down = false;
    const shutdown = async () => {
        if (shutting_down)
            adapter.exit(1);
        shutting_down = true;
        log.info('shutting down...');
        try {
            // Drain HTTP first (stop accepting + let in-flight requests finish)
            // before tearing down the backend, so a request still draining
            // never hits a closed DB.
            await server.shutdown();
            await built.close();
        }
        catch (error) {
            log.error('shutdown error:', error);
        }
        adapter.exit(0);
    };
    adapter.register_shutdown_signals(shutdown);
};
