'use client';

import { useEffect, useId, useRef, useState, type ComponentType, type FormEvent, type KeyboardEvent, type ReactNode } from 'react';
import { MAX_TEAM_NAME_LENGTH, POINTS, POWER_UP_TYPES, validateTeamName, type PowerUpType } from '@pintrush/engine';
import { LeaderboardList, PowerUpBadge, standingRows, teamRows, type PowerUpState } from '@/components/game';
import { AlertIcon, BoltIcon, GridIcon, InfoIcon, TrophyIcon, UsersIcon, XIcon } from '@/components/ui/icons';
import type { IconProps } from '@/components/ui/icons';
import { cn } from '@/lib/cn';
import { formatPoints, ordinal } from '@/lib/play/format';
import { MyBoard } from './parts';
import type { MenuTab, PlayCtx } from './types';

const TABS: { id: MenuTab; label: string; icon: ComponentType<IconProps> }[] = [
  { id: 'leaders', label: 'Ranks', icon: TrophyIcon },
  { id: 'teams', label: 'Teams', icon: UsersIcon },
  { id: 'board', label: 'Board', icon: GridIcon },
  { id: 'power', label: 'Power-ups', icon: BoltIcon },
  { id: 'rules', label: 'Scoring', icon: InfoIcon },
];

/**
 * Always-available bottom sheet (native <dialog>: focus is trapped, Esc and the backdrop close it,
 * focus returns to the menu button). Tabs follow the WAI-ARIA tabs pattern with arrow keys.
 */
export function MenuSheet({
  ctx,
  open,
  tab,
  onTab,
  onClose,
}: {
  ctx: PlayCtx;
  open: boolean;
  tab: MenuTab;
  onTab: (t: MenuTab) => void;
  onClose: () => void;
}) {
  const ref = useRef<HTMLDialogElement>(null);
  const uid = useId();
  const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);

  useEffect(() => {
    const d = ref.current;
    if (!d) return;
    if (open && !d.open) d.showModal();
    if (!open && d.open) d.close();
  }, [open]);

  function onTabKey(e: KeyboardEvent<HTMLButtonElement>, index: number) {
    const delta = e.key === 'ArrowRight' ? 1 : e.key === 'ArrowLeft' ? -1 : 0;
    let next = index + delta;
    if (e.key === 'Home') next = 0;
    if (e.key === 'End') next = TABS.length - 1;
    if (next === index && delta === 0 && e.key !== 'Home' && e.key !== 'End') return;
    e.preventDefault();
    const wrapped = (next + TABS.length) % TABS.length;
    onTab(TABS[wrapped]!.id);
    tabRefs.current[wrapped]?.focus();
  }

  return (
    <dialog
      ref={ref}
      aria-labelledby={`${uid}-title`}
      onClose={onClose}
      onClick={(e) => {
        if (e.target === ref.current) onClose();
      }}
      className="m-0 mt-auto max-h-[88dvh] w-full max-w-none overflow-hidden rounded-t-[1.75rem] border border-b-0 border-line-strong/70 bg-surface p-0 text-fg shadow-lift backdrop:bg-night/75 backdrop:backdrop-blur-sm sm:mx-auto sm:max-w-lg"
    >
      <div className="flex max-h-[88dvh] flex-col">
        <div className="flex items-center gap-2 px-4 pt-2">
          <span aria-hidden="true" className="absolute top-2 left-1/2 h-1.5 w-12 -translate-x-1/2 rounded-full bg-line-strong/70" />
          <h2 id={`${uid}-title`} className="flex-1 pt-3 font-display text-lg font-bold">
            Game menu
          </h2>
          <button
            type="button"
            onClick={onClose}
            aria-label="Close menu"
            className="mt-2 grid size-12 place-items-center rounded-xl text-muted hover:bg-white/[0.06] hover:text-fg"
          >
            <XIcon className="size-6" />
          </button>
        </div>
        <div role="tablist" aria-label="Menu sections" className="grid grid-cols-5 gap-1 border-b border-line px-2 pt-1">
          {TABS.map((t, i) => {
            const selected = t.id === tab;
            const Icon = t.icon;
            return (
              <button
                key={t.id}
                ref={(el) => {
                  tabRefs.current[i] = el;
                }}
                type="button"
                role="tab"
                id={`${uid}-tab-${t.id}`}
                aria-selected={selected}
                aria-controls={`${uid}-panel`}
                tabIndex={selected ? 0 : -1}
                onClick={() => onTab(t.id)}
                onKeyDown={(e) => onTabKey(e, i)}
                className={cn(
                  'flex min-h-14 flex-col items-center justify-center gap-1 border-b-2 text-[0.6875rem] font-semibold transition-colors',
                  selected ? 'border-cyan text-fg' : 'border-transparent text-muted hover:text-fg',
                )}
              >
                <Icon className={cn('size-5', selected ? 'text-cyan' : '')} />
                {t.label}
              </button>
            );
          })}
        </div>
        <div
          role="tabpanel"
          id={`${uid}-panel`}
          aria-labelledby={`${uid}-tab-${tab}`}
          tabIndex={0}
          className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pt-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] outline-none"
        >
          {open && tab === 'leaders' && <LeadersPanel ctx={ctx} />}
          {open && tab === 'teams' && <TeamsPanel ctx={ctx} />}
          {open && tab === 'board' && <BoardPanel ctx={ctx} />}
          {open && tab === 'power' && <PowerPanel ctx={ctx} />}
          {open && tab === 'rules' && <RulesPanel />}
        </div>
      </div>
    </dialog>
  );
}

function SectionTitle({ children }: { children: ReactNode }) {
  return <h3 className="mb-2 text-xs font-bold tracking-[0.14em] text-subtle uppercase">{children}</h3>;
}

function LeadersPanel({ ctx }: { ctx: PlayCtx }) {
  const { pub, me, snapshot } = ctx;
  const standings = pub?.leaderboard ?? snapshot.attract.nightlyTop;
  const rows = standingRows(standings, { highlightId: me?.playerId });
  const inTop = rows.some((r) => r.highlight);
  return (
    <div>
      {me && (
        <p className="mb-4 rounded-2xl border border-violet/55 bg-violet/15 px-4 py-3">
          <span className="block text-xs font-bold tracking-[0.14em] text-violet-soft uppercase">You</span>
          <span className="num font-display text-xl font-bold">
            {me.totals.rank ? `${ordinal(me.totals.rank)} · ` : ''}
            {formatPoints(me.totals.points)} pts
          </span>
          <span className="block text-sm text-muted">
            {me.totals.correct} correct · {me.totals.lines} {me.totals.lines === 1 ? 'line' : 'lines'}
            {me.totals.fullBoards > 0 && ` · ${me.totals.fullBoards} full ${me.totals.fullBoards === 1 ? 'board' : 'boards'}`}
          </span>
        </p>
      )}
      <SectionTitle>{pub?.mode === 'CHAMPIONSHIP' ? 'Championship top 10' : 'Tonight’s top 10'}</SectionTitle>
      <LeaderboardList rows={rows} label="Top ten players" />
      {me && !inTop && me.totals.rank !== null && <p className="mt-2 text-center text-sm text-muted">Keep going — the top 10 is in reach.</p>}
      <p className="mt-4 text-xs text-subtle">Ties: more correct answers, then faster total answer time, then the earlier last correct answer.</p>
    </div>
  );
}

function TeamPicker({ ctx }: { ctx: PlayCtx }) {
  const current = ctx.me?.teamName ?? null;
  const [name, setName] = useState(current ?? '');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const id = useId();

  async function submit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    if (busy) return;
    const v = validateTeamName(name);
    if (!v.ok) {
      setError(v.reason === 'PROFANITY' ? 'Let’s keep the team name friendly.' : v.message + '.');
      return;
    }
    setBusy(true);
    const res = await ctx.ctl.setTeam(v.nickname);
    setBusy(false);
    setError(res.ok ? null : (res.error ?? 'Could not save the team.'));
  }

  async function goSolo() {
    if (busy) return;
    setBusy(true);
    const res = await ctx.ctl.setTeam(null);
    setBusy(false);
    if (res.ok) setName('');
    setError(res.ok ? null : (res.error ?? 'Could not leave the team.'));
  }

  if (!ctx.me) return null;
  return (
    <form onSubmit={submit} className="rounded-2xl border border-cyan/40 bg-cyan/[0.07] p-4" noValidate>
      <label htmlFor={`${id}-team`} className="text-sm font-semibold">
        {current ? (
          <>
            You’re playing for <span className="text-cyan-soft">{current}</span>
          </>
        ) : (
          'Join your table’s team'
        )}
      </label>
      <p id={`${id}-hint`} className="mt-1 text-sm text-muted">
        Everyone at the table types the same name. Teams score the average of their players.
      </p>
      <div className="mt-3 flex gap-2">
        <input
          id={`${id}-team`}
          value={name}
          onChange={(e) => {
            setName(e.target.value);
            setError(null);
          }}
          maxLength={MAX_TEAM_NAME_LENGTH}
          autoComplete="off"
          autoCapitalize="words"
          spellCheck={false}
          enterKeyHint="done"
          placeholder="Team name"
          aria-invalid={error ? true : undefined}
          aria-describedby={`${id}-hint${error ? ` ${id}-error` : ''}`}
          className="block min-h-12 w-full min-w-0 flex-1 rounded-xl border-2 border-line-strong bg-night/70 px-3 text-base text-fg placeholder:text-subtle focus-visible:border-cyan focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-cyan/60"
        />
        <button
          type="submit"
          disabled={busy || name.trim() === '' || name.trim() === current}
          className="min-h-12 shrink-0 rounded-xl bg-cyan px-4 font-semibold text-on-bright disabled:opacity-50"
        >
          {busy ? 'Saving…' : current ? 'Switch' : 'Join'}
        </button>
      </div>
      {error && (
        <p id={`${id}-error`} role="alert" className="mt-2 flex items-start gap-1.5 text-sm font-medium text-danger-soft">
          <AlertIcon className="mt-0.5 size-4 shrink-0" /> {error}
        </p>
      )}
      {current && (
        <button
          type="button"
          onClick={() => void goSolo()}
          disabled={busy}
          className="mt-2 min-h-12 text-sm font-semibold text-violet-soft hover:text-fg disabled:opacity-50"
        >
          Leave the team and play solo
        </button>
      )}
    </form>
  );
}

function TeamsPanel({ ctx }: { ctx: PlayCtx }) {
  const { pub, me } = ctx;
  return (
    <div className="flex flex-col gap-6">
      <TeamPicker ctx={ctx} />
      {pub && (
        <section>
          <SectionTitle>Tables</SectionTitle>
          <LeaderboardList
            rows={teamRows(pub.teamLeaderboard, { highlightId: me?.teamId })}
            label="Team standings"
            emptyText="No tables yet — start one above and get your friends to join."
          />
        </section>
      )}
      {pub?.soloLeaderboard && pub.soloLeaderboard.length > 0 && (
        <section>
          <SectionTitle>Solo players</SectionTitle>
          <LeaderboardList
            rows={teamRows(pub.soloLeaderboard, { highlightId: me?.teamId ?? (me ? `solo:${me.playerId}` : null) })}
            label="Solo standings"
          />
        </section>
      )}
    </div>
  );
}

function BoardPanel({ ctx }: { ctx: PlayCtx }) {
  const board = ctx.me?.board;
  if (!board) {
    return (
      <p className="rounded-2xl border border-dashed border-line-strong/70 px-4 py-6 text-center text-muted">
        Your board appears during board rounds — The Rush, Music Mayhem and the Power Round.
      </p>
    );
  }
  const lit = board.tiles.filter((t) => t.activated).length;
  return (
    <div>
      <MyBoard ctx={ctx} />
      <p className="mt-3 text-center text-sm text-muted">
        {lit} of 9 lit · {board.lines.length} {board.lines.length === 1 ? 'line' : 'lines'}
        {board.fullBoard && ' · full board!'}
      </p>
      <p className="mt-3 text-sm text-subtle">
        Answer a category’s question right to light its square. Three in a row is a line (+250 and a power-up token); all nine is a full board (+500).
      </p>
    </div>
  );
}

function powerState(type: PowerUpType, ctx: PlayCtx): PowerUpState {
  const p = ctx.me?.powerUps;
  if (!p) return 'locked';
  if (type === 'DOUBLE_DOWN' && p.doubleDownOn) return 'active';
  if (p.used.includes(type)) return 'used';
  return p.tokens > 0 ? 'ready' : 'locked';
}

function PowerPanel({ ctx }: { ctx: PlayCtx }) {
  const p = ctx.me?.powerUps;
  if (p && !p.enabled) return <p className="text-muted">Power-ups are switched off for this game.</p>;
  const tokens = p?.tokens ?? 0;
  return (
    <div>
      <p className="mb-4 flex items-center gap-3 rounded-2xl border border-lime/45 bg-lime/10 px-4 py-3">
        <BoltIcon className="size-6 shrink-0 text-lime" />
        <span>
          <span className="num block font-display text-xl font-bold">
            {tokens} {tokens === 1 ? 'token' : 'tokens'}
          </span>
          <span className="block text-sm text-muted">
            Complete a line to earn one. Each power-up works once per {ctx.pub?.mode === 'CHAMPIONSHIP' ? 'championship' : 'night'}.
          </span>
        </span>
      </p>
      <ul className="flex flex-col gap-2">
        {POWER_UP_TYPES.map((type) => (
          <li key={type}>
            <PowerUpBadge type={type} state={powerState(type, ctx)} showDetails />
          </li>
        ))}
      </ul>
      <p className="mt-4 text-sm text-subtle">Buttons appear on your game screen whenever a power-up can be used.</p>
    </div>
  );
}

function RulesPanel() {
  const rows: [string, string][] = [
    ['Correct answer', `+${POINTS.CORRECT_STANDARD}`],
    ['Correct, hard question', `+${POINTS.CORRECT_DIFFICULT}`],
    ['Speed bonus (faster = more)', `+0–${POINTS.SPEED_BONUS_MAX}`],
    ['Line (row, column or diagonal)', `+${POINTS.LINE}`],
    ['Full board', `+${POINTS.FULL_BOARD}`],
    ['Table challenge win (each player)', `+${POINTS.TABLE_CHALLENGE}`],
    ['Double Down right / wrong', `+${POINTS.DOUBLE_DOWN_WIN} / −${Math.abs(POINTS.DOUBLE_DOWN_LOSS)}`],
    ['Wrong or no answer', '0'],
  ];
  return (
    <div>
      <table className="w-full text-left text-sm">
        <caption className="sr-only">How scoring works</caption>
        <thead>
          <tr className="text-xs tracking-[0.14em] text-subtle uppercase">
            <th scope="col" className="pb-2 font-bold">
              Event
            </th>
            <th scope="col" className="pb-2 text-right font-bold">
              Points
            </th>
          </tr>
        </thead>
        <tbody className="divide-y divide-line/70">
          {rows.map(([event, pts]) => (
            <tr key={event}>
              <th scope="row" className="py-2.5 pr-3 font-medium">
                {event}
              </th>
              <td className="num py-2.5 text-right font-display font-bold">{pts}</td>
            </tr>
          ))}
        </tbody>
      </table>
      <ul className="mt-5 flex list-disc flex-col gap-2 pl-5 text-sm text-muted">
        <li>Every round has 9 category questions plus a wildcard: get the wildcard right to claim any square.</li>
        <li>Join late? You can jump into a round until question 3 opens.</li>
        <li>Speed is timed by the game server (with a small allowance for slow Wi-Fi), never by your phone’s clock.</li>
        <li>Teams score the average of players who answered at least once.</li>
      </ul>
    </div>
  );
}
