import { getUsers, getUserByEmail } from 'officerdb';

// Who this platform knows about, loaded once at launch.
//
// This is a CLASSIFIER, never a gate. Nothing here decides whether a request is allowed — the signin
// handler still does its own database lookup and is the only thing that authenticates. The snapshot
// exists so the audit middleware can answer one question cheaply, on the hot path of an unauthenticated
// endpoint: "is the name in this request even one of ours?" A wrong answer costs a spurious log line or
// a missing one, never access.
//
// It holds emails AND usernames because both are identities a caller can present, and both are unique
// columns on `users`. Matching is case-insensitive: an attempt on OWNER@example.com is the owner's own
// address typed differently, not a stranger, and logging it as an intrusion would train the owner to
// ignore the file.
//
// Single-user is a hard invariant here, so this set has two entries in practice. It is a Set rather
// than a string because that invariant is the platform's, not this file's, and a Set costs nothing.

let snapshot: Set<string> | null = null;

const key = (identity: string) => identity.trim().toLowerCase();

const identitiesOf = (user: { email: string; username: string | null }): string[] =>
  [user.email, user.username].filter((value): value is string => typeof value === 'string' && !!value.trim());

/** Read every account into memory. Called once at launch; safe to call again to refresh. */
export async function loadKnownUsers(): Promise<number> {
  const users = await getUsers();
  snapshot = new Set(users.flatMap(identitiesOf).map(key));
  return snapshot.size;
}

/**
 * Fold a newly created account in. Bootstrap is the only path that mints one, and without this the
 * owner's very first sign-in after creating the account would be logged as a stranger — the snapshot
 * having been taken while the user table was still empty.
 */
export function rememberUser(user: { email: string; username: string | null }): void {
  if (!snapshot) return;
  for (const identity of identitiesOf(user)) snapshot.add(key(identity));
}

/** How many identities are loaded, or null if the launch-time load has not succeeded. */
export const knownIdentityCount = (): number | null => snapshot?.size ?? null;

/**
 * Whether an identity belongs to an account. Falls back to the database when the launch-time load
 * never ran or failed — a Postgres blip at boot must not turn every later login into an alert.
 */
export async function isKnownIdentity(identity: string): Promise<boolean> {
  const wanted = key(identity);
  if (!wanted) return false;
  if (snapshot) return snapshot.has(wanted);

  const user = await getUserByEmail(identity).catch(() => undefined);
  return !!user;
}
