import { neonConfig } from '@neondatabase/serverless';
import { sql } from 'drizzle-orm';
import type { NeonHttpDatabase } from 'drizzle-orm/neon-http';
import type { TransactionalDatabase } from '@platform-modules/db';
import { createNeonHttpClient } from '@platform-modules/db/neon-http';
import { createNeonServerlessClient } from '@platform-modules/db/neon-serverless';
import { composedSchema as schema } from '@/server/db/schema-composed';
import { scrubErrorForLog } from '@/server/observability/pii-scrub.js';

export type DoDbClient = Omit<NeonHttpDatabase<typeof schema>, 'transaction'>;
export type DoTxDbClient = TransactionalDatabase<typeof schema>;

const WARM_MAX_ATTEMPTS = 3;
const WARM_BACKOFF_MS = [250, 750, 1500] as const;

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export function createScheduledDb(env: { DATABASE_URL: string }): DoDbClient {
  return createNeonHttpClient({
    connectionString: env.DATABASE_URL,
    schema,
  });
}

export function createTransactionalDb(env: { DATABASE_URL: string }): DoTxDbClient {
  neonConfig.poolQueryViaFetch = true;
  if (typeof WebSocket !== 'undefined') {
    neonConfig.webSocketConstructor = WebSocket;
  }
  return createNeonServerlessClient({
    connectionString: env.DATABASE_URL,
    schema,
  });
}

/** @deprecated Use createScheduledDbService from @/server/services/db. Removed in getDb migration. */
export { createScheduledDb as getDb };

/** @deprecated Use createTransactionalDbService from @/server/services/db. Removed in getDb migration. */
export { createTransactionalDb as getTransactionalDb };

export async function warmDb(db: DoDbClient): Promise<void> {
  let lastErr: unknown;
  for (let attempt = 0; attempt < WARM_MAX_ATTEMPTS; attempt++) {
    try {
      await db.execute(sql`SELECT 1`);
      return;
    } catch (err) {
      lastErr = err;
      if (attempt < WARM_MAX_ATTEMPTS - 1) {
        await sleep(WARM_BACKOFF_MS[attempt]!);
      }
    }
  }
  console.error(
    JSON.stringify({
      event: 'scheduled_db_warm_failed',
      attempts: WARM_MAX_ATTEMPTS,
      error: scrubErrorForLog(lastErr),
    }),
  );
}
