import { neonConfig, Pool as NeonPool } from '@neondatabase/serverless';
import { createNeonServerlessClient } from '@platform-modules/db/neon-serverless';
import type { Querier, TransactionalDatabase } from '@platform-modules/db';
import { drizzle as neonDrizzle, type NeonDatabase } from 'drizzle-orm/neon-serverless';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { captureCaught } from '@/lib/observability';
import { composedSchema as schema } from './schema-composed.js';

export type DrizzleDb = NeonDatabase<typeof schema>;
export type DrizzleClient = Querier<typeof schema>;
export type TxDrizzleClient = TransactionalDatabase<typeof schema>;

const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '::1']);

const LOCAL_POOL_OPTIONS = {
  max: 4,
  maxUses: 1,
  allowExitOnIdle: true,
  idleTimeoutMillis: 1000,
  connectionTimeoutMillis: 5000,
  maxLifetimeSeconds: 60,
} as const;

function createLocalPool(connectionString: string): Pool {
  return new Pool({
    connectionString,
    ...LOCAL_POOL_OPTIONS,
  });
}

function createLocalDb(pool: Pool): DrizzleDb {
  return drizzle(pool, { schema }) as unknown as DrizzleDb;
}

export function isLocalDatabaseUrl(connectionString: string): boolean {
  try {
    const url = new URL(connectionString);
    const queryHost = url.searchParams.get('host');
    const hostname = decodeURIComponent(url.hostname).replace(/^\[|\]$/g, '');
    const decodedQueryHost = queryHost ? decodeURIComponent(queryHost) : '';
    return (
      LOCAL_HOSTS.has(hostname) || hostname.startsWith('/') || decodedQueryHost.startsWith('/')
    );
  } catch (err) {
    captureCaught(
      new Error(`database URL parsing failed: ${err instanceof Error ? err.name : 'unknown'}`),
      {
        scope: 'server.db.client.isLocalDatabaseUrl',
        severity: 'warning',
      },
    );
    return false;
  }
}

function configureNeonDriver(): void {
  neonConfig.poolQueryViaFetch = true;
  if (typeof WebSocket !== 'undefined') {
    neonConfig.webSocketConstructor = WebSocket;
  }
}

export function createNeonRequestDbScope(env: { DATABASE_URL: string }): {
  db: DrizzleDb;
  close: () => Promise<void>;
} {
  if (isLocalDatabaseUrl(env.DATABASE_URL)) {
    const pool = createLocalPool(env.DATABASE_URL);
    return {
      db: createLocalDb(pool),
      close: () => pool.end(),
    };
  }

  configureNeonDriver();
  const pool = new NeonPool({ connectionString: env.DATABASE_URL });
  const db = neonDrizzle(pool, { schema }) as DrizzleDb;
  return { db, close: () => pool.end() };
}

export function createRequestDb(env: { DATABASE_URL: string }): DrizzleDb {
  if (isLocalDatabaseUrl(env.DATABASE_URL)) {
    return createLocalDb(createLocalPool(env.DATABASE_URL));
  }

  configureNeonDriver();
  return createNeonServerlessClient({
    connectionString: env.DATABASE_URL,
    schema,
  });
}

export { createRequestDb as getDb };
