'use client';

import type { Standing } from '@pintrush/engine';
import { useEffect, useMemo, useState, type ReactNode } from 'react';
import { useGameConnection, useWakeLock } from '@/lib/game';
import { cn } from '@/lib/cn';
import { currentPublic, isPaused, sceneKey, tvScreenFor, type TvScreen } from '@/lib/tv/screen';
import { TvEnvProvider, type TvEnv } from './context';
import { ConnectionPill, PausedOverlay } from './parts/Overlays';
import { AttractScreen } from './screens/AttractScreen';
import { CompleteScreen } from './screens/CompleteScreen';
import { IntroScreen } from './screens/IntroScreen';
import { LobbyScreen } from './screens/LobbyScreen';
import { PendingScreen } from './screens/PendingScreen';
import { QuestionScreen } from './screens/QuestionScreen';
import { RevealScreen } from './screens/RevealScreen';
import { RoundOverScreen } from './screens/RoundOverScreen';
import { SceneBoundary } from './SceneBoundary';
import { TvMessageScreen } from './TvMessageScreen';
import { TvStage } from './TvStage';
import s from './tv.module.css';

/** Server closed the socket on purpose ('offline'): a TV has nobody to press reload, so retry. */
const OFFLINE_RETRY_MS = 5_000;
/** After this long without any state, say so instead of an endless "connecting". */
const SLOW_CONNECT_MS = 8_000;

export interface TvAppProps {
  roomId: string;
  tvToken: string;
  qr: ReactNode;
  joinCode: string;
  joinUrlText: string;
  venueName: string;
  timeZone: string;
}

const SCREEN_LABEL: Record<TvScreen, string> = {
  connecting: 'Connecting',
  denied: 'TV link not valid',
  attract: 'Scan to play',
  lobby: 'Championship lobby',
  intro: 'Round intro',
  pending: 'Up next',
  open: 'Question open',
  locked: 'Answers locked',
  revealed: 'Answer revealed',
  roundOver: 'Round complete',
  complete: 'Champion',
};

/** Live TV display for one room: connects with the TV token and renders the scene for the state. */
export function TvApp({ roomId, tvToken, qr, joinCode, joinUrlText, venueName, timeZone }: TvAppProps) {
  const { conn, state } = useGameConnection({ auth: () => ({ tvToken }) }, roomId);
  useWakeLock(state.status !== 'denied');

  useEffect(() => {
    if (state.status !== 'offline' || !conn) return;
    const timer = setTimeout(() => conn.reconnect(), OFFLINE_RETRY_MS);
    return () => clearTimeout(timer);
  }, [state.status, conn]);

  const [slow, setSlow] = useState(false);
  const waiting = !state.snapshot && !state.public;
  useEffect(() => {
    if (!waiting) return;
    const timer = setTimeout(() => setSlow(true), SLOW_CONNECT_MS);
    return () => {
      clearTimeout(timer);
      setSlow(false);
    };
  }, [waiting]);

  const pub = currentPublic(state);
  const screen = tvScreenFor(state);

  // Standings as they were before the current reveal, so the leaderboard can shuffle into place.
  const [beforeReveal, setBeforeReveal] = useState<{ questionId: string; standings: Standing[] } | null>(null);
  const phase = pub?.round?.phase;
  const questionId = pub?.question?.id ?? null;
  const leaderboard = pub?.leaderboard;
  useEffect(() => {
    if (!questionId || !leaderboard || (phase !== 'OPEN' && phase !== 'LOCKED')) return;
    setBeforeReveal({ questionId, standings: leaderboard });
  }, [phase, questionId, leaderboard]);
  const previousStandings = screen === 'revealed' && beforeReveal && beforeReveal.questionId === questionId ? beforeReveal.standings : null;

  const env = useMemo<TvEnv>(() => ({ conn, qr, joinCode, joinUrlText, venueName, timeZone }), [conn, qr, joinCode, joinUrlText, venueName, timeZone]);

  let scene: ReactNode;
  switch (screen) {
    case 'denied':
      scene = (
        <TvMessageScreen
          tone="error"
          title="This TV link isn’t working"
          body="Open the TV link from the PintRush host dashboard on this screen. Old links stop working when a host resets them."
          detail={state.error ? `Code: ${state.error.code}` : undefined}
        />
      );
      break;
    case 'connecting':
      scene = (
        <TvMessageScreen
          title={slow ? 'Still trying to reach the game' : `Connecting to ${venueName}…`}
          body={slow ? 'Check this screen’s internet connection. The TV keeps retrying on its own.' : 'The game will appear in a moment.'}
        />
      );
      break;
    case 'attract':
      scene = <AttractScreen pub={pub} snapshot={state.snapshot} />;
      break;
    case 'lobby':
      scene = pub && <LobbyScreen pub={pub} snapshot={state.snapshot} />;
      break;
    case 'intro':
      scene = pub && <IntroScreen pub={pub} />;
      break;
    case 'pending':
      scene = pub && <PendingScreen pub={pub} />;
      break;
    case 'open':
    case 'locked':
      scene = pub && <QuestionScreen pub={pub} locked={screen === 'locked'} />;
      break;
    case 'revealed':
      scene = pub && <RevealScreen pub={pub} previousStandings={previousStandings} />;
      break;
    case 'roundOver':
      scene = pub && <RoundOverScreen pub={pub} />;
      break;
    case 'complete':
      scene = pub && <CompleteScreen pub={pub} snapshot={state.snapshot} />;
      break;
  }

  return (
    <TvEnvProvider value={env}>
      <TvStage label={`${venueName} TV display`}>
        <div key={sceneKey(screen, pub)} className={cn('absolute inset-0', s.sceneIn)}>
          <SceneBoundary fallback={<TvMessageScreen title={`Live at ${venueName}`} body={`Scan or go to ${joinUrlText} to play. The next screen is on its way.`} />}>
            {scene}
          </SceneBoundary>
        </div>
        {isPaused(pub) && <PausedOverlay />}
        <ConnectionPill status={state.status} />
        {/* One short, answer-free announcement per scene for assistive tech (e.g. in the demo iframe). */}
        <p className="sr-only" aria-live="polite">
          {SCREEN_LABEL[screen]}
        </p>
      </TvStage>
    </TvEnvProvider>
  );
}
