/**
 * Boot-time reconciliation of configuration into the database.
 *
 * This exists to close a gap that was genuinely confusing: `PIG_INVITE_CODE`
 * looked like it gated signup, but signup validates against the `invites`
 * table, so setting the variable only flipped a label in the UI. An operator
 * would set it, hand the code to a colleague, and watch them be rejected.
 *
 * Now the variable is reconciled into a real, reusable invite row at every
 * boot. Changing the value in the environment and restarting revokes the old
 * code and issues the new one, which is the behaviour anyone setting an
 * environment variable would reasonably expect.
 */
import { and, eq, isNull, ne } from 'drizzle-orm';
import { createHash } from 'node:crypto';
import type { Database } from '@pig/db';
import { invites } from '@pig/db';
import type { Config } from '../lib/config';

/** The label marking the invite owned by configuration rather than by a person. */
const ENV_INVITE_LABEL = 'env:PIG_INVITE_CODE';

export async function reconcileInviteCode(config: Config, db: Database): Promise<void> {
  const code = config.PIG_INVITE_CODE?.trim();

  // No code configured: revoke any previously configured one, so removing the
  // variable actually closes the door rather than leaving it ajar.
  if (!code) {
    const revoked = await db
      .update(invites)
      .set({ revokedAt: new Date() })
      .where(and(eq(invites.scopeNote, ENV_INVITE_LABEL), isNull(invites.revokedAt)))
      .returning({ id: invites.id });
    if (revoked.length) {
      console.log('[pig] PIG_INVITE_CODE is unset — revoked the previous configured invite.');
    }
    return;
  }

  const codeHash = createHash('sha256').update(code).digest('hex');

  const [existing] = await db
    .select()
    .from(invites)
    .where(and(eq(invites.codeHash, codeHash), isNull(invites.revokedAt)))
    .limit(1);

  if (existing) {
    // Already present and live. Nothing to do — restarts must be idempotent.
    return;
  }

  // Revoke any older configured invite before issuing the new one, so a
  // rotated code genuinely replaces its predecessor.
  await db
    .update(invites)
    .set({ revokedAt: new Date() })
    .where(
      and(
        eq(invites.scopeNote, ENV_INVITE_LABEL),
        isNull(invites.revokedAt),
        ne(invites.codeHash, codeHash),
      ),
    );

  await db.insert(invites).values({
    codeHash,
    scopeNote: ENV_INVITE_LABEL,
    // Effectively unlimited. A shared workspace code is meant to onboard a
    // team, not one person; per-person invites are issued from the UI with
    // their own limits.
    //
    // One million rather than Number.MAX_SAFE_INTEGER, which overflows a
    // 32-bit integer column and fails at insert.
    usesRemaining: 1_000_000,
  });

  console.log('[pig] Configured invite code is active. Rotate it by changing the variable.');
}
