/**
 * Shared sign-in flow for /host/login and /admin/login (called from their server actions).
 * Credentials are checked by @pintrush/db (scrypt, constant time, dummy hash for unknown e-mails);
 * this module adds input validation, brute-force throttling and the session cookie.
 */
import { headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { authenticatePlatformAdmin, authenticateVenueUser, getDb, MAX_PASSWORD_LENGTH } from '@pintrush/db';
import { clientIp } from '../rate-limit';
import { safeNextPath } from './cookies';
import { describeWait, LoginThrottle, maskEmail, throttleKeys } from './login-throttle';
import { endSession, startAdminSession, startHostSession } from './session';
import type { SessionKind } from './session-token';

export type SignInState = { status: 'idle' } | { status: 'error'; message: string; email: string };

const CredentialsSchema = z.object({
  email: z.string().trim().min(3).max(254).pipe(z.email()),
  password: z.string().min(1).max(MAX_PASSWORD_LENGTH),
});

// Survives dev hot reloads so a reload can't be used to reset the counters. (The key is versioned
// with the LoginThrottle API so a hot reload never picks up an instance of an older class.)
const globalForThrottle = globalThis as unknown as { __pintrushLoginThrottleV2?: LoginThrottle };
const throttle = (globalForThrottle.__pintrushLoginThrottleV2 ??= new LoginThrottle());

const MISMATCH = 'That email and password don’t match. Check them and try again.';

export async function signIn(kind: SessionKind, formData: FormData): Promise<SignInState> {
  const rawEmail = typeof formData.get('email') === 'string' ? String(formData.get('email')) : '';
  const parsed = CredentialsSchema.safeParse({ email: rawEmail, password: formData.get('password') });
  const email = rawEmail.trim().slice(0, 254);
  if (!parsed.success) return { status: 'error', message: 'Enter the email address and password for your account.', email };

  const now = Date.now();
  const keys = throttleKeys(kind, clientIp(await headers()), parsed.data.email);
  const gate = throttle.check(keys, now);
  if (!gate.allowed) {
    return {
      status: 'error',
      message: `Too many sign-in attempts. For your security, please wait ${describeWait(gate.retryAfterMs)} and try again.`,
      email,
    };
  }
  // The account is being guessed from many networks: slow every attempt down, never refuse it.
  if (gate.delayMs > 0) await new Promise((resolve) => setTimeout(resolve, gate.delayMs));

  const failed = (): SignInState => {
    if (throttle.recordFailure(keys, now).accountTripped) {
      console.warn(`[auth] Many failed ${kind} sign-ins for ${maskEmail(parsed.data.email)} from several networks; slowing attempts for this account.`);
    }
    return { status: 'error', message: MISMATCH, email };
  };

  const db = getDb();
  if (kind === 'host') {
    const user = await authenticateVenueUser(db, parsed.data.email, parsed.data.password);
    if (!user) return failed();
    const venue = await db.venue.findUnique({ where: { id: user.venueId }, select: { status: true } });
    if (!venue || venue.status === 'CANCELLED') {
      return { status: 'error', message: 'This venue’s PintRush account is closed. Contact PintRush support to reopen it.', email };
    }
    await startHostSession({ sub: user.id, venueId: user.venueId, role: user.role, ver: user.version });
  } else {
    const admin = await authenticatePlatformAdmin(db, parsed.data.email, parsed.data.password);
    if (!admin) return failed();
    await startAdminSession({ sub: admin.id, passwordHash: admin.passwordHash });
  }
  // redirect() throws, so it must stay outside any try/catch.
  redirect(safeNextPath(kind, formData.get('next')));
}

export async function signOut(kind: SessionKind): Promise<never> {
  await endSession(kind);
  redirect(kind === 'host' ? '/host/login' : '/admin/login');
}
