import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';

const { POSTGRES_URL } = process.env;

if (!POSTGRES_URL) {
  throw new Error('POSTGRES_URL environment variable is required');
}

const client = postgres(POSTGRES_URL);
export const db = drizzle(client, { schema });

/**
 * Block until Postgres answers, or give up after `timeoutMs`.
 *
 * `postgres()` above is LAZY — it opens no socket until the first query — so nothing here fails at
 * import time when the database is not up yet. That is the right default and it has a cost: startup
 * work that queries fires, fails once, and is swallowed by whatever `.catch()` it was written with.
 *
 * That is not hypothetical. `server.tsx` runs `initQueue()` and `cleanupOnStartup()` as
 * fire-and-forget promises, and the second marks jobs that were interrupted by the last restart and
 * promotes the queued backlog. If Postgres is a few seconds behind — which is exactly what happens on
 * a reboot, when pm2's resurrect races Docker starting the container — both log a line and do nothing.
 * Interrupted jobs then stay marked running forever, because the only thing that would have corrected
 * them already ran.
 *
 * So: wait, rather than try once. Callers that genuinely cannot proceed without the database await
 * this first; request handlers do not, since by then it is either up or the request fails honestly.
 *
 * Bounded, and it resolves false rather than throwing on timeout. An unbounded wait here would hold a
 * process open with no way to tell whether it is starting or hung, and the caller is better placed to
 * decide what "gave up" means than this function is.
 */
export async function waitForDatabase(timeoutMs = 60_000): Promise<boolean> {
  const started = Date.now();
  let announced = false;

  for (;;) {
    try {
      await client`select 1`;
      if (announced) console.log('[db] Postgres is up');
      return true;
    } catch (err) {
      if (Date.now() - started >= timeoutMs) {
        console.error(
          `[db] Postgres did not answer within ${Math.round(timeoutMs / 1000)}s:`,
          err instanceof Error ? err.message : err,
        );
        return false;
      }
      if (!announced) {
        console.log('[db] waiting for Postgres…');
        announced = true;
      }
      await new Promise((r) => setTimeout(r, 1_000));
    }
  }
}
