import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import { sql } from 'drizzle-orm';
import type { Querier } from '@platform-modules/db';
import {
  claimInstall,
  finalizeInstall,
  isSiteConfigured,
  INSTALL_CLAIM_TTL_MS,
  SITE_CONFIGURED_KEY,
} from './install.js';
import { getSetting, setSetting, type SettingsSchema } from './settings.js';
import { startPg } from './pg-harness.js';

describe('isSiteConfigured', () => {
  it('is true only for strict boolean true', () => {
    expect(isSiteConfigured(true)).toBe(true);
  });

  it('rejects claiming state, string true, number, null, undefined', () => {
    expect(isSiteConfigured({ state: 'claiming' })).toBe(false);
    expect(isSiteConfigured('true')).toBe(false);
    expect(isSiteConfigured(1)).toBe(false);
    expect(isSiteConfigured(null)).toBe(false);
    expect(isSiteConfigured(undefined)).toBe(false);
  });
});

describe('claimInstall (real Postgres)', () => {
  let db: Querier<SettingsSchema>;
  let createIsolatedDb: () => Querier<SettingsSchema>;
  let stop: (() => Promise<void>) | undefined;

  beforeAll(async () => {
    const pg = await startPg();
    db = pg.db;
    createIsolatedDb = pg.createIsolatedDb;
    stop = pg.stop;
  }, 120_000);

  afterAll(async () => {
    await stop?.();
  }, 30_000);

  beforeEach(async () => {
    await db.execute(sql`DELETE FROM site_settings WHERE key = ${SITE_CONFIGURED_KEY}`);
  });

  it('wins on a fresh site and stores claiming lease', async () => {
    const now = new Date('2026-06-20T12:00:00.000Z');
    expect(await claimInstall(db, now, 'postgres')).toBe(true);
    expect(await getSetting(db, SITE_CONFIGURED_KEY)).toEqual({
      state: 'claiming',
      at: now.toISOString(),
    });
  });

  it('loses when site is already configured (INVARIANT-INSTALL-1)', async () => {
    await setSetting(db, SITE_CONFIGURED_KEY, true);
    expect(await claimInstall(db, new Date(), 'postgres')).toBe(false);
  });

  it('loses on a fresh claiming lease (concurrent double-submit)', async () => {
    await setSetting(db, SITE_CONFIGURED_KEY, {
      state: 'claiming',
      at: new Date().toISOString(),
    });
    expect(await claimInstall(db, new Date(), 'postgres')).toBe(false);
  });

  it('wins on a stale claiming lease (INVARIANT-INSTALL-2 takeover)', async () => {
    const now = new Date('2026-06-20T13:00:00.000Z');
    const staleAt = new Date(now.getTime() - INSTALL_CLAIM_TTL_MS - 60_000);
    await setSetting(db, SITE_CONFIGURED_KEY, { state: 'claiming', at: staleAt.toISOString() });
    expect(await claimInstall(db, now, 'postgres')).toBe(true);
    expect(await getSetting(db, SITE_CONFIGURED_KEY)).toEqual({
      state: 'claiming',
      at: now.toISOString(),
    });
  });

  it('concurrency: exactly one winner across separate connections', async () => {
    const racers = Array.from({ length: 8 }, () => createIsolatedDb());
    const now = new Date('2026-06-20T14:00:00.000Z');
    const results = await Promise.all(racers.map((conn) => claimInstall(conn, now, 'postgres')));
    expect(results.filter(Boolean)).toHaveLength(1);
    expect(results.filter((r) => !r)).toHaveLength(7);
  });

  it('finalizeInstall flips site_configured to true', async () => {
    const now = new Date('2026-06-20T15:00:00.000Z');
    expect(await claimInstall(db, now, 'postgres')).toBe(true);
    await finalizeInstall(db);
    expect(isSiteConfigured(await getSetting(db, SITE_CONFIGURED_KEY))).toBe(true);
  });
});
