import fs from "node:fs";
import pg from "pg";
import { drizzle } from "drizzle-orm/node-postgres";
import * as schema from "./schema/index.js";

export type Database = ReturnType<typeof createDatabase>["db"];

let sharedPool: pg.Pool | undefined;

function resolveSsl(url: string): pg.PoolConfig["ssl"] | undefined {
  const caPath = process.env.DATABASE_CA_CERT;
  if (caPath && fs.existsSync(caPath)) {
    return { ca: fs.readFileSync(caPath, "utf8"), rejectUnauthorized: true };
  }
  if (/sslmode=(require|verify)/.test(url)) {
    return { rejectUnauthorized: false };
  }
  return undefined;
}

function stripSslmode(url: string): string {
  try {
    const u = new URL(url);
    u.searchParams.delete("sslmode");
    return u.toString();
  } catch {
    return url.replace(/[?&]sslmode=[^&]+/, "").replace(/\?$/, "");
  }
}

/**
 * Skapar en databasklient. API och worker delar mönster men äger varsin pool.
 * DATABASE_URL pekar på appens separata databas med egen minimalprivilegie-användare
 * (minsta möjliga privilegier, spec §52).
 */
export function createDatabase(connectionString?: string) {
  const isTest = process.env.NODE_ENV === "test" || process.env.VITEST !== undefined;
  const rawUrl = connectionString ?? (isTest ? process.env.TEST_DATABASE_URL : process.env.DATABASE_URL);

  if (!rawUrl) {
    if (isTest) {
      throw new Error(
        "Sätt TEST_DATABASE_URL till en dedikerad testdatabas. Tester får aldrig använda DATABASE_URL.",
      );
    }
    throw new Error("Missing DATABASE_URL. Set it in your .env or environment before starting the app.");
  }

  const url = stripSslmode(rawUrl);
  const pool = new pg.Pool({
    connectionString: url,
    max: Number(process.env.DATABASE_POOL_MAX ?? 10),
    idleTimeoutMillis: 30_000,
    connectionTimeoutMillis: 10_000,
    ssl: resolveSsl(rawUrl),
  });

  const db = drizzle(pool, { schema, casing: "snake_case" });
  return { db, pool };
}

/** Singleton för processer som bara behöver en anslutning. */
export function getDatabase() {
  if (!sharedPool) {
    const { db, pool } = createDatabase();
    sharedPool = pool;
    sharedDb = db;
  }
  return sharedDb!;
}
let sharedDb: Database | undefined;

export async function closeDatabase(): Promise<void> {
  if (sharedPool) {
    await sharedPool.end();
    sharedPool = undefined;
    sharedDb = undefined;
  }
}

export { schema };
