import type { PublicState } from '@pintrush/engine';
import type { RoomSnapshot } from '@pintrush/protocol';
import type { ConnectionStatus } from '../game/connection';

/** Which full-screen scene the TV shows. Overlays (paused, reconnecting) sit on top of these. */
export type TvScreen =
  | 'connecting'
  | 'denied'
  | 'attract'
  | 'lobby'
  | 'intro'
  | 'pending'
  | 'open'
  | 'locked'
  | 'revealed'
  | 'roundOver'
  | 'complete';

export interface TvScreenInput {
  status: ConnectionStatus;
  snapshot: RoomSnapshot | null;
  public: PublicState | null;
}

/** The public view to render: live broadcasts win over the (possibly older) snapshot copy. */
export function currentPublic(input: Pick<TvScreenInput, 'snapshot' | 'public'>): PublicState | null {
  return input.public ?? input.snapshot?.public ?? null;
}

/**
 * Pick the scene for the current state. Purely a function of the latest complete views, never of
 * message order. Reveal data is only ever shown when the server actually sent it (REVEALED with a
 * `reveal` payload); anything inconsistent falls back to a scene that shows less, not more.
 */
export function tvScreenFor(input: TvScreenInput): TvScreen {
  if (input.status === 'denied') return 'denied';
  if (!input.snapshot && !input.public) return 'connecting';
  const pub = currentPublic(input);
  if (!pub) return 'attract';
  if (pub.status === 'COMPLETED') return 'complete';
  if (pub.status === 'WAITING') return 'lobby';
  const round = pub.round;
  if (!round) return 'attract';
  switch (round.phase) {
    case 'INTRO':
      return 'intro';
    case 'PENDING':
      return pub.question ? 'pending' : 'intro';
    case 'OPEN':
      // An OPEN question without its prompt cannot be shown; hold on the teaser instead.
      return pub.question?.prompt && pub.question.options ? 'open' : pub.question ? 'pending' : 'intro';
    case 'LOCKED':
      return pub.question?.prompt ? 'locked' : 'intro';
    case 'REVEALED':
      if (pub.reveal && pub.question && pub.reveal.questionId === pub.question.id) return 'revealed';
      return pub.question?.prompt ? 'locked' : 'intro';
    case 'ROUND_OVER':
      return 'roundOver';
    default:
      return 'attract';
  }
}

/** Paused overlay: the host paused a running session. */
export function isPaused(pub: PublicState | null): boolean {
  return pub?.status === 'PAUSED';
}

/** Late join is open until question index `limit` OPENS (ARCHITECTURE §2.8: "until question 3 opens"). */
export function lateJoinOpen(round: PublicState['round'], limit = 2): boolean {
  if (!round) return true;
  if (round.kind === 'FINAL_RUSH') return false;
  if (round.phase === 'ROUND_OVER') return false;
  if (round.qIndex < limit) return true;
  return round.qIndex === limit && (round.phase === 'INTRO' || round.phase === 'PENDING');
}

/** A stable key for the current scene, so scene transitions animate once per question/phase. */
export function sceneKey(screen: TvScreen, pub: PublicState | null): string {
  const q = pub?.question?.id ?? 'none';
  const r = pub?.round?.id ?? 'none';
  switch (screen) {
    case 'pending':
    case 'open':
    case 'revealed':
      return `${screen}:${q}`;
    // Locked is the same layout as open: keep the node so only the lock beat animates.
    case 'locked':
      return `open:${q}`;
    case 'intro':
    case 'roundOver':
      return `${screen}:${r}`;
    default:
      return screen;
  }
}
