// [nexus] Auto-create a bootstrap_ceo invite on first boot so the web UI can
// redirect the first user to /invite/{token} without any terminal command.
// Mirrors the logic in cli/src/commands/auth-bootstrap-ceo.ts; the CLI command
// remains available for headless / SSH-only setups.

import { createHash, randomBytes } from "node:crypto";
import { and, count, eq, gt, isNull } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import { instanceUserRoles, invites } from "@paperclipai/db";

function hashToken(token: string) {
  return createHash("sha256").update(token).digest("hex");
}

function createInviteToken() {
  return `pcp_bootstrap_${randomBytes(24).toString("hex")}`;
}

/**
 * If no instance_admin user exists, create a bootstrap_ceo invite and return
 * the relative path the browser should navigate to in order to create the
 * first admin account. Returns null if an admin already exists (no-op).
 *
 * Safe to call on every boot — when an admin already exists it's a single
 * count query with no writes.
 */
export async function ensureBootstrapInvite(db: Db): Promise<string | null> {
  const adminCount = await db
    .select({ count: count() })
    .from(instanceUserRoles)
    .where(eq(instanceUserRoles.role, "instance_admin"))
    .then((rows) => Number(rows[0]?.count ?? 0));

  if (adminCount > 0) return null;

  const now = new Date();
  // Revoke any stale live invites so our fresh token is the only match.
  // We can't recover the raw token from the stored hash, so the previous
  // invite is unreachable and must be replaced on restart.
  await db
    .update(invites)
    .set({ revokedAt: now, updatedAt: now })
    .where(
      and(
        eq(invites.inviteType, "bootstrap_ceo"),
        isNull(invites.revokedAt),
        isNull(invites.acceptedAt),
        gt(invites.expiresAt, now),
      ),
    );

  const token = createInviteToken();
  const expiresHours = 72;
  await db.insert(invites).values({
    inviteType: "bootstrap_ceo",
    tokenHash: hashToken(token),
    allowedJoinTypes: "human",
    expiresAt: new Date(Date.now() + expiresHours * 60 * 60 * 1000),
    invitedByUserId: "system",
  });

  return `/invite/${token}`;
}
