import type { Server } from 'node:http';
import app from './app.js';
import prisma, { connectDB } from './config/db.js';
import { env } from './config/env.js';
import logger from './config/logger.js';

let server: Server | undefined;

// Without these, an error thrown outside the request cycle (e.g. in a
// fire-and-forget callback) never reaches errorHandler.ts and crashes the
// process with a bare stack trace on stderr — nothing in logs/. Log it
// properly, then exit; the process manager (PM2/Docker/...) restarts us,
// since the process may be in a corrupted state after either of these.
process.on('uncaughtException', (error: Error) => {
  logger.error('Uncaught exception — shutting down', { message: error.message, stack: error.stack });
  process.exit(1);
});

process.on('unhandledRejection', (reason: unknown) => {
  const error = reason instanceof Error ? reason : new Error(String(reason));
  logger.error('Unhandled promise rejection — shutting down', { message: error.message, stack: error.stack });
  process.exit(1);
});

async function startServer(): Promise<void> {
  try {
    await connectDB();
    server = app.listen(env.PORT, () => {
      logger.info(`Server running at http://localhost:${env.PORT}`);
    });
    server.on('error', async (error: NodeJS.ErrnoException) => {
      logger.error('Server failed to start', { code: error.code || error.name });
      await prisma.$disconnect();
      process.exit(1);
    });
  } catch (error) {
    const err = error as NodeJS.ErrnoException;
    logger.error('Database connection failed. Check PostgreSQL and DATABASE_URL.', { code: err.code || err.name });
    await prisma.$disconnect();
    process.exit(1);
  }
}

function shutdown(): void {
  const timeout = setTimeout(() => process.exit(1), 10000);
  timeout.unref();

  if (!server) {
    prisma.$disconnect().finally(() => process.exit(0));
    return;
  }

  server.close(async () => {
    await prisma.$disconnect();
    process.exit(0);
  });
}

process.once('SIGINT', shutdown);
process.once('SIGTERM', shutdown);

startServer();
