'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { playerHref } from '@/lib/playerHref';
import { searchTeams, teamHref } from '@/lib/teams';
import PlayerAvatar from '@/components/vyndr/PlayerAvatar';
import { type HeadshotSport } from '@/lib/playerHeadshot';

/**
 * SearchModal — S6 (A1 board). Global search over players + teams.
 *
 *   Players: /api/players/search per sport (the canonical nameKey resolver,
 *            S60 — MLB statsapi list, others cache-only). Debounced 250ms.
 *   Teams:   the static registry in lib/teams.js — no fetch.
 *
 * ⌘K / Ctrl-K opens it (registered in GlobalHosts, which also exposes
 * window.__search for the Nav icon + the mobile More sheet). Grouped results
 * (PLAYERS / TEAMS), arrow-key navigation, Enter → playerHref / team hub.
 * Data is mono; chrome may glitch, results never do.
 */

const PLAYER_SPORTS = ['MLB', 'NBA', 'WNBA'] as const;
const SPORT_COLOR: Record<string, string> = {
  mlb: 'var(--s-mlb, #1E90FF)',
  nba: 'var(--s-nba, #E94B3C)',
  wnba: 'var(--s-wnba, #FFB347)',
};

interface ResultItem {
  kind: 'player' | 'team';
  label: string;
  sub: string; // team / abbr context
  sport: string;
  href: string;
  // Wave 2A — MLBAM headshot id (MLB only; NBA/WNBA search ids are synthetic).
  playerId?: string | number | null;
}

export default function SearchModal({ open, onClose }: { open: boolean; onClose: () => void }) {
  const router = useRouter();
  const [q, setQ] = useState('');
  const [players, setPlayers] = useState<ResultItem[]>([]);
  const [sel, setSel] = useState(0);
  const inputRef = useRef<HTMLInputElement>(null);
  const seqRef = useRef(0);

  // Teams are a static-list match — synchronous, no fetch.
  const teams = useMemo<ResultItem[]>(
    () =>
      searchTeams(q, 6).map((t: { name: string; abbr: string; sport: string }) => ({
        kind: 'team' as const,
        label: t.name,
        sub: t.abbr,
        sport: t.sport,
        href: teamHref(t),
      })),
    [q],
  );

  const flat = useMemo(() => [...players, ...teams], [players, teams]);

  // Debounced player fan-out. A stale response never overwrites a newer one.
  useEffect(() => {
    if (!open) return;
    const query = q.trim();
    if (query.length < 2) {
      setPlayers([]);
      return;
    }
    const seq = ++seqRef.current;
    const timer = setTimeout(async () => {
      try {
        const responses = await Promise.all(
          PLAYER_SPORTS.map((sp) =>
            fetch(`/api/players/search?sport=${sp}&q=${encodeURIComponent(query)}`)
              .then((r) => (r.ok ? r.json() : { players: [] }))
              .then((d) => ({ sport: sp.toLowerCase(), players: Array.isArray(d?.players) ? d.players : [] }))
              .catch(() => ({ sport: sp.toLowerCase(), players: [] })),
          ),
        );
        if (seq !== seqRef.current) return;
        const seen = new Set<string>();
        const merged: ResultItem[] = [];
        for (const res of responses) {
          for (const p of res.players as Array<{ full_name?: string; team?: string; id?: string | number }>) {
            const name = String(p.full_name || '').trim();
            if (!name) continue;
            const key = `${name.toLowerCase()}|${res.sport}`;
            if (seen.has(key)) continue;
            seen.add(key);
            // MLB's search id is the real MLBAM id → headshot. Other sports'
            // ids are synthetic → guarded out → monogram.
            const playerId = res.sport === 'mlb' && p.id != null && /^\d+$/.test(String(p.id)) ? p.id : null;
            merged.push({
              kind: 'player',
              label: name,
              sub: p.team ? String(p.team) : '',
              sport: res.sport,
              href: playerHref(name, res.sport),
              playerId,
            });
            if (merged.length >= 9) break;
          }
          if (merged.length >= 9) break;
        }
        setPlayers(merged);
      } catch {
        if (seq === seqRef.current) setPlayers([]);
      }
    }, 250);
    return () => clearTimeout(timer);
  }, [q, open]);

  // Reset + focus on open.
  useEffect(() => {
    if (open) {
      setQ('');
      setPlayers([]);
      setSel(0);
      const t = setTimeout(() => inputRef.current?.focus(), 20);
      return () => clearTimeout(t);
    }
  }, [open]);

  useEffect(() => {
    setSel(0);
  }, [q]);

  const go = useCallback(
    (item: ResultItem | undefined) => {
      if (!item) return;
      onClose();
      router.push(item.href);
    },
    [onClose, router],
  );

  const onKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setSel((s) => Math.min(s + 1, Math.max(flat.length - 1, 0)));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setSel((s) => Math.max(s - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      go(flat[sel]);
    } else if (e.key === 'Escape') {
      e.preventDefault();
      onClose();
    }
  };

  if (!open) return null;

  const Group = ({ title, items, offset }: { title: string; items: ResultItem[]; offset: number }) => {
    if (items.length === 0) return null;
    return (
      <div>
        <div className="mono" style={{ padding: '10px 16px 4px', fontSize: 10, fontWeight: 800, letterSpacing: '0.12em', color: 'var(--text-2)' }}>
          {title}
        </div>
        {items.map((it, i) => {
          const idx = offset + i;
          const active = idx === sel;
          return (
            <button
              key={`${it.kind}-${it.sport}-${it.label}`}
              type="button"
              onClick={() => go(it)}
              onMouseEnter={() => setSel(idx)}
              className="mono"
              style={{
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                gap: 10,
                width: '100%',
                textAlign: 'left',
                padding: '9px 16px',
                background: active ? 'color-mix(in srgb, var(--g-a) 12%, transparent)' : 'transparent',
                border: 'none',
                borderLeft: `2px solid ${active ? 'var(--g-a)' : 'transparent'}`,
                cursor: 'pointer',
                color: 'var(--text-0, #E8E8F0)',
                fontSize: 13,
              }}
            >
              {/* ROW-GRAMMAR: never truncate a name — wrap instead. Wave 2A adds
                  the player identity avatar (real headshot / branded monogram). */}
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
                {it.kind === 'player' && (
                  <PlayerAvatar
                    name={it.label}
                    sport={it.sport as HeadshotSport}
                    playerId={it.playerId}
                    team={it.sub || null}
                    size={26}
                  />
                )}
                <span style={{ fontWeight: 700, whiteSpace: 'normal' }}>{it.label}</span>
              </span>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
                {it.sub && <span style={{ fontSize: 11, color: 'var(--text-1)' }}>{it.sub}</span>}
                <span style={{ fontSize: 9.5, fontWeight: 800, letterSpacing: '0.08em', color: SPORT_COLOR[it.sport] || 'var(--text-1)' }}>
                  {it.sport.toUpperCase()}
                </span>
              </span>
            </button>
          );
        })}
      </div>
    );
  };

  return (
    <div
      className="fade-in"
      role="dialog"
      aria-modal="true"
      aria-label="Search players and teams"
      onClick={onClose}
      style={{
        position: 'fixed',
        inset: 0,
        zIndex: 90,
        background: 'rgba(6,6,11,.72)',
        backdropFilter: 'blur(4px)',
        WebkitBackdropFilter: 'blur(4px)',
        display: 'flex',
        alignItems: 'flex-start',
        justifyContent: 'center',
        padding: '12vh 16px 16px',
      }}
    >
      <div
        onClick={(e) => e.stopPropagation()}
        className="scanlines"
        style={{
          width: '100%',
          maxWidth: 560,
          background: 'var(--bg-1)',
          border: '1px solid var(--border-hi)',
          borderRadius: 12,
          overflow: 'hidden',
        }}
      >
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', borderBottom: '1px solid var(--border)' }}>
          <span className="mono" aria-hidden style={{ color: 'var(--g-a)', fontSize: 13 }}>›</span>
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => setQ(e.target.value)}
            onKeyDown={onKeyDown}
            placeholder="Search players, teams"
            aria-label="Search players and teams"
            className="mono"
            style={{
              flex: 1,
              background: 'transparent',
              border: 'none',
              outline: 'none',
              color: 'var(--text-0, #fff)',
              fontSize: 14,
              letterSpacing: '0.02em',
            }}
          />
          <kbd className="mono" style={{ fontSize: 9.5, color: 'var(--text-2)', border: '1px solid var(--border)', borderRadius: 4, padding: '2px 5px' }}>
            ESC
          </kbd>
        </div>
        <div style={{ maxHeight: '52vh', overflowY: 'auto', paddingBottom: 6 }}>
          {q.trim().length < 2 ? (
            <p className="mono" style={{ padding: '18px 16px', fontSize: 11.5, color: 'var(--text-2)' }}>
              Type two characters. ↑↓ to move, Enter to open.
            </p>
          ) : flat.length === 0 ? (
            <p className="mono" style={{ padding: '18px 16px', fontSize: 11.5, color: 'var(--text-2)' }}>
              No matches on the wire.
            </p>
          ) : (
            <>
              <Group title="PLAYERS" items={players} offset={0} />
              <Group title="TEAMS" items={teams} offset={players.length} />
            </>
          )}
        </div>
      </div>
    </div>
  );
}
