import fs from 'node:fs';
import path from 'node:path';

import EmbeddedPostgres from 'embedded-postgres';

import { CONNECTION_URL, DATABASES, DATA_DIRECTORY, PID_FILE, POSTGRES_CONFIG } from './config.js';

/**
 * Starts a real PostgreSQL server for local development, without Docker.
 *
 * Docker Compose remains the documented path (see README). This exists because
 * Docker Desktop is frequently unavailable on managed institutional Windows
 * workstations, and a prototype nobody can run is not a prototype. It downloads
 * an official PostgreSQL build into a git-ignored directory and serves it on
 * the same port with the same credentials as the compose service, so nothing
 * downstream — DATABASE_URL, migrations, seeds — has to change.
 *
 * This is a developer convenience. It is never used for deployment.
 */
async function main(): Promise<void> {
  const alreadyInitialised = fs.existsSync(path.join(DATA_DIRECTORY, 'PG_VERSION'));
  const postgres = new EmbeddedPostgres({ ...POSTGRES_CONFIG });

  if (!alreadyInitialised) {
    console.log('Initialising a new PostgreSQL data directory (first run only)…');
    fs.mkdirSync(DATA_DIRECTORY, { recursive: true });
    await postgres.initialise();
  }

  console.log(`Starting PostgreSQL on port ${POSTGRES_CONFIG.port}…`);
  await postgres.start();

  // `createDatabase` throws when the database already exists, which is the
  // normal case on every run after the first.
  for (const database of DATABASES) {
    try {
      await postgres.createDatabase(database);
      console.log(`  created database "${database}"`);
    } catch {
      console.log(`  database "${database}" already exists`);
    }
  }

  fs.writeFileSync(PID_FILE, String(process.pid), 'utf8');

  console.log('');
  console.log('PostgreSQL is running. Leave this terminal open.');
  console.log(`  DATABASE_URL=${CONNECTION_URL}`);
  console.log('');
  console.log('  In a second terminal:  pnpm db:migrate && pnpm db:seed && pnpm dev');
  console.log('  Stop this server:      Ctrl+C  (or `pnpm db:local:stop` elsewhere)');

  // The server runs as a child of this process, so the process must stay alive
  // for the database to stay up — the same contract as `docker compose up`
  // without `-d`. Shut down cleanly so the data directory is never left in a
  // state that needs recovery on the next start.
  let shuttingDown = false;
  const shutdown = async (signal: string): Promise<void> => {
    if (shuttingDown) return;
    shuttingDown = true;
    console.log(`\nReceived ${signal}. Stopping PostgreSQL…`);
    try {
      await postgres.stop();
      if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);
      console.log('PostgreSQL stopped cleanly.');
      process.exit(0);
    } catch (error) {
      console.error('PostgreSQL did not stop cleanly:', error);
      process.exit(1);
    }
  };

  for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {
    process.on(signal, () => void shutdown(signal));
  }

  // Keep the event loop occupied indefinitely.
  await new Promise<never>(() => {});
}

main().catch((error: unknown) => {
  console.error('Failed to start the local PostgreSQL server.');
  console.error(error instanceof Error ? error.message : error);
  console.error('');
  console.error('If port 5433 is already in use, stop the other server or use Docker instead:');
  console.error('  docker compose up -d');
  process.exit(1);
});
