// Firing door actions without ever blocking the queue of people at the door.
//
// The UI flashes green and clears the input the moment staff taps; the write
// happens here, in the background, with retries. Venue wifi drops constantly, so
// every action carries an idempotency key: a retry that actually succeeded the
// first time returns the original result instead of double-charging anyone.

import { doorApi, type DoorCheckinRequest, type DoorCheckinResponse } from '@/lib/api';

/** UUID per action. crypto.randomUUID needs a secure context; fall back when absent. */
export function newIdempotencyKey(): string {
  const cryptoRef = typeof crypto !== 'undefined' ? crypto : undefined;
  if (cryptoRef?.randomUUID) return cryptoRef.randomUUID();
  return `door-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
}

// Roughly 5 seconds of retrying in total — long enough to ride out a wifi blip,
// short enough that staff learn about a real failure while the person is still
// in front of them.
const RETRY_DELAYS_MS = [400, 1200, 3000];

/**
 * Errors worth retrying are the ones a retry can fix: network failures, gateway
 * errors, rate limits. A 400 "ticket belongs to a different event" will fail
 * identically forever, so it surfaces immediately.
 */
function isRetryable(error: any): boolean {
  const status = error?.status;
  if (typeof status === 'number') return status >= 500 || status === 408 || status === 429;
  // No status at all means the request never reached the server (fetch rejects
  // with a TypeError when the connection drops) — exactly the case to retry.
  return true;
}

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

export async function submitDoorAction(
  eventId: string,
  body: DoorCheckinRequest,
): Promise<DoorCheckinResponse> {
  let lastError: any;
  for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
    try {
      return await doorApi.checkin(eventId, body);
    } catch (error: any) {
      lastError = error;
      if (attempt === RETRY_DELAYS_MS.length || !isRetryable(error)) break;
      await sleep(RETRY_DELAYS_MS[attempt]);
    }
  }
  throw lastError;
}

export async function undoDoorAction(eventId: string, idempotencyKey: string): Promise<void> {
  await doorApi.undo(eventId, idempotencyKey);
}
