/**
 * Browser-only helpers for the phone: nickname memory, haptics and waiting for a socket.
 * Every storage/API access is guarded: private mode, blocked storage and old browsers must
 * never break the game.
 */
import type { GameConnection } from '../game/connection';
import { lastNickname } from '../game/storage';

const NICK_PREFIX = 'pintrush:nick:';

/**
 * Remember the nickname used in a room separately from the tokens, so it can still be prefilled
 * after tokens were cleared (game over, link expired). Only the nickname is kept — no other data.
 */
export function rememberNickname(roomId: string, nickname: string): void {
  try {
    window.localStorage.setItem(NICK_PREFIX + roomId, nickname);
  } catch {
    // storage unavailable: the token list still carries the nickname for this tab
  }
}

export function recallNickname(roomId: string): string | null {
  const fromToken = lastNickname(roomId);
  if (fromToken) return fromToken;
  try {
    return window.localStorage.getItem(NICK_PREFIX + roomId);
  } catch {
    return null;
  }
}

/**
 * Short haptic cue where supported (Android Chrome); silently ignored elsewhere (iOS Safari).
 * Chrome refuses (and logs an intervention) until the page has had a user tap, e.g. a reveal
 * right after a reload, so only buzz once the user has interacted.
 */
export function vibrate(pattern: number | number[]): void {
  try {
    if (typeof navigator === 'undefined' || typeof navigator.vibrate !== 'function') return;
    if (navigator.userActivation && !navigator.userActivation.hasBeenActive) return;
    navigator.vibrate(pattern);
  } catch {
    // some browsers throw without a user gesture
  }
}

/** Resolves true once the connection is live, false after `timeoutMs` (bad bar Wi-Fi). */
export function waitForConnected(conn: GameConnection, timeoutMs: number): Promise<boolean> {
  if (conn.getState().status === 'connected') return Promise.resolve(true);
  return new Promise((resolve) => {
    let unsubscribe: () => void = () => undefined;
    const timer = setTimeout(() => {
      unsubscribe();
      resolve(false);
    }, timeoutMs);
    unsubscribe = conn.subscribe(() => {
      const status = conn.getState().status;
      if (status === 'connected' || status === 'denied') {
        clearTimeout(timer);
        unsubscribe();
        resolve(status === 'connected');
      }
    });
  });
}

/** One idempotency key per logical action (e.g. "answer:<questionId>"), stable across retries and re-renders. */
export class KeyRing {
  private readonly keys = new Map<string, string>();

  for(scope: string): string {
    let key = this.keys.get(scope);
    if (!key) {
      key = crypto.randomUUID();
      this.keys.set(scope, key);
      // Bounded: a long always-on night asks hundreds of questions.
      if (this.keys.size > 200) this.keys.delete(this.keys.keys().next().value as string);
    }
    return key;
  }
}
