import { buildApp } from '../../app/build-app.js';
import { ConfigError, loadConfig } from '../config/config.js';
import { createPool } from '../db/pool.js';
import { createFlagStore } from '../../modules/flags/index.js';
import { createLogger } from '../logging/logger.js';
import { getLogBroadcaster } from '../logging/log-broadcaster.js';

// Surface fatal signals so the next monolith run can see why the previous
// instance died. Without these, SIGTERM/SIGKILL from a harness or the OS
// leaves no trace in the runtime log.
process.on('uncaughtException', (err) => {
  // eslint-disable-next-line no-console
  console.error('uncaughtException', err);
});
process.on('unhandledRejection', (reason) => {
  // eslint-disable-next-line no-console
  console.error('unhandledRejection', reason);
});

let config;
try {
  config = loadConfig(process.env);
} catch (error) {
  if (error instanceof ConfigError) {
    // Config is invalid: fail fast and loud before anything else starts.
    console.error(error.message);
  } else {
    console.error('Failed to load configuration', error);
  }
  process.exit(1);
}

const broadcaster = getLogBroadcaster();
const logger = createLogger({ level: config.logLevel, broadcaster });
const pool = createPool(config.databaseUrl);

try {
  const app = await buildApp({
    logger,
    flags: createFlagStore(config.flags),
    pool,
    cookieSecure: config.cookieSecure,
  });
  await app.listen({ port: config.port, host: config.host });
  logger.info({ port: config.port, host: config.host }, 'HTTP server listening');

  // Heartbeat: log process health every 60s so crash diagnosis has trailing evidence.
  const startTime = Date.now();
  setInterval(() => {
    const uptimeSec = Math.floor((Date.now() - startTime) / 1000);
    logger.info({ uptimeSec, memMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024) }, 'heartbeat');
  }, 60_000).unref();
} catch (error) {
  logger.error({ err: error }, 'Failed to start HTTP server');
  await pool.end();
  process.exit(1);
}
