'use client';

import { useState } from 'react';
import type { PropRowProp, PropRowResult, Tier } from '@/components/PropRow';
import SportBadge from '@/components/vyndr/SportBadge';
import SectionHead from '@/components/vyndr/SectionHead';
import BookChip from '@/components/vyndr/BookChip';
import { detectBestLines } from '@/lib/slateAdapter';
// Session 19 — PlayerCard groups props by player so a single player
// with 4 props renders as ONE card with their headshot + 4 stat
// lines, instead of 4 independent stripes that repeat the name.
import PlayerCard, { groupPropsByPlayer } from '@/components/PlayerCard';

/**
 * GameCard — one game in the Slate (Session 13). Header with teams +
 * time + venue + sport emoji; expandable list of player props
 * underneath, each a PropRow.
 *
 * State minimalism: this component only manages "show more props"
 * expansion. The graded-props Map and the "is this prop loading right
 * now" boolean both live on the Slate (one source of truth for the
 * grading queue).
 */

export type SlateSport = 'nba' | 'wnba' | 'mlb' | 'soccer';

const SPORT_ACCENT: Record<SlateSport, string> = {
  nba: '#E94B3C',
  wnba: '#FFB347',
  mlb: '#1E90FF',
  soccer: '#00D4A0',
};

// Session 24 — game-level book-by-book lines from Tank01. One row per
// sportsbook (bet365 / betmgm / caesars …). All fields optional/null —
// books publish lines independently, so we render only what exists.
export interface GameLineBook {
  homeML?: string | null;
  awayML?: string | null;
  total?: string | null;
  homeSpread?: string | null;
  awaySpread?: string | null;
}
export interface GameLines {
  homeTeam?: string | null;
  awayTeam?: string | null;
  books: Record<string, GameLineBook>;
}

export type GameStatus = 'pre' | 'in' | 'post';

// Session 25 — a computed streak for a player whose team is in this game.
export interface GameStreak {
  player: string;
  team?: string | null;
  description: string;
  currentStreak: number;
}

export interface GameCardProps {
  sport: SlateSport;
  homeTeam: string;
  awayTeam: string;
  gameTime?: string;        // ISO timestamp — empty when status is unknown
  venue?: string;
  context?: string;         // 'Group A · Matchday 1', 'Game 4', etc.
  props: PropRowProp[];
  gradedProps: Map<string, PropRowResult>;
  loadingKey?: string | null;  // propRowKey of the prop currently grading
  errorByKey?: Record<string, string | undefined>;
  tier?: Tier;
  onGrade: (prop: PropRowProp) => void;
  onUpgrade?: () => void;
  defaultVisible?: number;   // how many props to show before "+ N more"
  // Session 24 — all-day intelligence layers, all optional. A game card
  // shows whatever exists: schedule status/score (ESPN), game lines
  // (Tank01), and props (odds-api) — nothing replaces anything else.
  status?: GameStatus;
  score?: { home: number; away: number } | null;
  gameLines?: GameLines | null;
  // Session 25 — streaks for players in THIS game, matched by team in the
  // Slate. Renders inline below the props/lines so the streak context
  // lives with the game it belongs to.
  streaks?: GameStreak[];
}

// Map ESPN status → a compact, human badge. 'in' is live; 'post' is final.
function statusBadge(status?: GameStatus, score?: { home: number; away: number } | null) {
  if (status === 'in') return { label: 'LIVE', color: '#FF4D4D', score };
  if (status === 'post') return { label: 'FINAL', color: '#6B6B7B', score };
  return null; // 'pre' or unknown → no badge; the tip-off time carries it
}

function formatTime(iso?: string) {
  if (!iso) return '';
  try {
    const d = new Date(iso);
    return d.toLocaleString(undefined, {
      weekday: 'short', month: 'short', day: 'numeric',
      hour: 'numeric', minute: '2-digit',
    });
  } catch {
    return iso;
  }
}

/**
 * Session 19 — produce an ESPN/DK-style team abbreviation from a
 * full team name. The odds-api returns full names like
 * "Phoenix Mercury" / "Boston Celtics"; the design calls for the
 * compact city/club shorthand in the header. We hand-curate the few
 * names that don't reduce well from a "take the first word"
 * heuristic (St. Louis, Los Angeles, etc.) and otherwise fall back
 * to the first three letters of the second word (so "Boston
 * Celtics" → "BOS" and "Real Madrid" → "RM"). Soccer clubs are
 * shorter and rarely follow city naming, so we just upper-case the
 * full name up to 6 chars.
 */
const TEAM_ABBR_OVERRIDES: Record<string, string> = {
  'Los Angeles Lakers': 'LAL',
  'Los Angeles Clippers': 'LAC',
  'Los Angeles Sparks': 'LAS',
  'Los Angeles Angels': 'LAA',
  'Los Angeles Dodgers': 'LAD',
  'San Francisco Giants': 'SF',
  'San Diego Padres': 'SD',
  'St. Louis Cardinals': 'STL',
  'New York Knicks': 'NYK',
  'New York Yankees': 'NYY',
  'New York Mets': 'NYM',
  'New York Liberty': 'NYL',
  'New Orleans Pelicans': 'NOP',
  'Oklahoma City Thunder': 'OKC',
  'Golden State Warriors': 'GSW',
  'Portland Trail Blazers': 'POR',
  'San Antonio Spurs': 'SA',
  'Las Vegas Aces': 'LV',
  'Washington Mystics': 'WAS',
  'Washington Wizards': 'WAS',
  'Washington Nationals': 'WSH',
  'Tampa Bay Rays': 'TB',
  'Kansas City Royals': 'KC',
  'Toronto Blue Jays': 'TOR',
  'Toronto Raptors': 'TOR',
  'Chicago White Sox': 'CWS',
  'Chicago Cubs': 'CHC',
};

export function teamAbbr(fullName: string, sport: SlateSport): string {
  if (!fullName) return '';
  if (TEAM_ABBR_OVERRIDES[fullName]) return TEAM_ABBR_OVERRIDES[fullName];
  if (sport === 'soccer') {
    // Clubs are typically one short name (Arsenal, Liverpool) or two
    // ("Real Madrid"). Compose initials when there are 2+ words,
    // otherwise truncate.
    const words = fullName.trim().split(/\s+/);
    if (words.length >= 2) return words.map((w) => w[0]).join('').toUpperCase().slice(0, 4);
    return fullName.slice(0, 6).toUpperCase();
  }
  // US team-name pattern: "<City> <Nickname>" — take the nickname's
  // first three letters (Boston Celtics → CEL → bumped to BOS via
  // the override table for the well-known cases). For names we
  // didn't override, the city-prefix usually carries more identity.
  const words = fullName.trim().split(/\s+/);
  if (words.length >= 2) {
    // Two-word names: prefer the city (first word) → first 3 letters.
    return words[0].slice(0, 3).toUpperCase();
  }
  return fullName.slice(0, 4).toUpperCase();
}

// Bloomberg line cell (Session 36): best = green tint + green left border,
// worst = subtle red. The #1 visual upgrade (§13).
function LineCell({ value, best, worst }: { value: string; best?: boolean; worst?: boolean }) {
  return (
    <div
      className="mono"
      style={{
        padding: '7px 6px',
        textAlign: 'center',
        fontSize: 12.5,
        fontWeight: best ? 700 : 500,
        color: best ? 'var(--g-a)' : worst ? '#ff8a8a' : 'var(--text-0)',
        background: best ? 'rgba(0,212,160,.13)' : worst ? 'rgba(255,82,82,.07)' : 'transparent',
        borderLeft: best ? '2px solid var(--g-a)' : worst ? '2px solid rgba(255,82,82,.4)' : '2px solid transparent',
        borderRadius: 3,
      }}
    >
      {value}
    </div>
  );
}

export default function GameCard(props: GameCardProps) {
  const {
    sport, homeTeam, awayTeam, gameTime, venue, context,
    props: propList, gradedProps, loadingKey, errorByKey,
    tier = 'free', onGrade, onUpgrade,
    defaultVisible = 4,
    status, score, gameLines, streaks,
  } = props;
  const [expanded, setExpanded] = useState(false);
  const badge = statusBadge(status, score);
  // Session 36 — best/worst line detection (Bloomberg pattern) via slateAdapter.
  const lineRows = gameLines?.books ? detectBestLines(gameLines.books) : [];
  const streakRows = (streaks || []).filter((s) => s && s.player && s.description);

  // Session 19 — visibility budget now applies to PLAYERS, not raw
  // props. Showing the first 4 prop rows that all belonged to the
  // same player meant the user only saw one player's card before
  // hitting "+ more." Grouping by player first lines up the budget
  // with how the UI actually reads.
  const playerGroups = groupPropsByPlayer(propList);
  // Stable alphabetical ordering — matches Slate's groupByGame sort.
  playerGroups.sort((a, b) => a.player.localeCompare(b.player));
  const visiblePlayerGroups = expanded ? playerGroups : playerGroups.slice(0, defaultVisible);
  const hiddenPlayerCount = playerGroups.length - visiblePlayerGroups.length;
  const accent = SPORT_ACCENT[sport];

  return (
    <article
      className="surface"
      style={{
        background: 'var(--bg-2, #12121A)',
        border: '1px solid var(--border, #1A1A24)',
        borderRadius: 8,
        overflow: 'hidden',
      }}
    >
      <header
        style={{
          padding: '16px 20px',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'flex-start',
          gap: 12,
          borderLeft: `3px solid ${accent}`,
        }}
      >
        <div style={{ minWidth: 0, flex: 1 }}>
          {/* Session 19 — team-abbreviation header: bold abbreviations
              with the full names underneath in the meta line, plus a
              sport-color badge to the right. ESPN/DK pattern.
              Session 26 — larger/bolder abbreviations for hierarchy. */}
          <div
            style={{
              display: 'flex',
              alignItems: 'center',
              gap: 11,
              fontSize: 18,
              fontWeight: 800,
              color: 'var(--text-0, #F0F0F5)',
              letterSpacing: '-0.02em',
            }}
          >
            <SportBadge sport={sport} size="sm" />
            <span className="mono" style={{ letterSpacing: '0.04em' }}>
              {teamAbbr(awayTeam, sport)}
            </span>
            <span style={{ color: 'var(--text-tertiary, #6B6B7B)', fontWeight: 400, fontSize: 13 }}>
              vs
            </span>
            <span className="mono" style={{ letterSpacing: '0.04em' }}>
              {teamAbbr(homeTeam, sport)}
            </span>
          </div>
          <div
            style={{
              marginTop: 4,
              fontSize: 12,
              color: 'var(--text-secondary, #8A8A9A)',
              whiteSpace: 'nowrap',
              overflow: 'hidden',
              textOverflow: 'ellipsis',
            }}
          >
            {awayTeam} <span style={{ color: 'var(--text-tertiary, #6B6B7B)' }}>@</span> {homeTeam}
          </div>
          <div
            className="mono"
            style={{
              marginTop: 4,
              fontSize: 11,
              color: 'var(--text-tertiary, #6B6B7B)',
              letterSpacing: '0.06em',
              textTransform: 'uppercase',
            }}
          >
            {[formatTime(gameTime), venue, context].filter(Boolean).join(' · ') || ' '}
          </div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 6 }}>
          {/* Session 24 — live/final status + score (ESPN schedule). */}
          {badge && (
            <span
              className="mono"
              style={{
                fontSize: 10,
                fontWeight: 800,
                letterSpacing: '0.08em',
                color: '#0A0A0F',
                background: badge.color,
                padding: '3px 8px',
                borderRadius: 4,
                whiteSpace: 'nowrap',
              }}
            >
              {badge.label}{badge.score ? ` · ${badge.score.away}–${badge.score.home}` : ''}
            </span>
          )}
          <div
            className="mono"
            style={{
              fontSize: 10,
              color: 'var(--text-tertiary, #6B6B7B)',
              background: 'rgba(255,255,255,0.04)',
              padding: '4px 8px',
              borderRadius: 999,
              whiteSpace: 'nowrap',
            }}
          >
            {propList.length} prop{propList.length === 1 ? '' : 's'}
          </div>
        </div>
      </header>

      {/* Session 24 — game lines strip (Tank01). Book-by-book moneyline,
          spread, total. Renders only when lines exist; never blocks the
          card. The brand edge is props, but lines give immediate action
          even when props haven't been published. */}
      {lineRows.length > 0 && (
        <div
          style={{
            padding: '12px 20px 14px',
            borderTop: '1px solid var(--border, #1A1A24)',
            background: 'rgba(255,255,255,0.015)',
          }}
        >
          <SectionHead style={{ marginBottom: 10 }}>
            GAME LINES <span style={{ color: 'var(--text-2)' }}>· {lineRows.length} BOOK{lineRows.length === 1 ? '' : 'S'}</span>
          </SectionHead>
          <div style={{ display: 'grid', gridTemplateColumns: '72px 1fr 1fr 1fr', gap: '2px 6px', alignItems: 'center' }}>
            <div className="label" style={{ fontSize: 10 }}>BOOK</div>
            <div className="label" style={{ fontSize: 10, textAlign: 'center' }}>{teamAbbr(awayTeam, sport)} ML</div>
            <div className="label" style={{ fontSize: 10, textAlign: 'center' }}>{teamAbbr(homeTeam, sport)} ML</div>
            <div className="label" style={{ fontSize: 10, textAlign: 'center' }}>O/U</div>
            {lineRows.map((r) => (
              <span key={r.book} style={{ display: 'contents' }}>
                {/* Session 43 — brand-colored book chip (was plain grey text). */}
                <div style={{ paddingLeft: 2, alignSelf: 'center' }}><BookChip book={r.book} size="sm" showName /></div>
                <LineCell value={r.awayML} best={r.bestAway} worst={r.worstAway} />
                <LineCell value={r.homeML} best={r.bestHome} worst={r.worstHome} />
                <LineCell value={r.ou} />
              </span>
            ))}
          </div>
        </div>
      )}

      {propList.length === 0 ? (
        <p
          style={{
            padding: '14px 20px',
            color: 'var(--text-tertiary, #6B6B7B)',
            fontSize: 12,
            // Subtle, informational — not an error. Left-aligned so it
            // reads as a quiet status line, not a centered empty wall.
            textAlign: 'left',
            opacity: 0.8,
          }}
        >
          Props for this game aren&apos;t published yet.
        </p>
      ) : (
        <div>
          {visiblePlayerGroups.map((g) => (
            <PlayerCard
              key={g.player}
              player={g.player}
              sport={sport}
              // Per-player team would require a roster lookup that
              // the current odds endpoints don't expose. Leave team
              // empty — the game card header already shows both
              // teams, so the user has the context.
              team={undefined}
              props={g.props}
              gradedProps={gradedProps}
              loadingKey={loadingKey}
              errorByKey={errorByKey}
              tier={tier}
              onGrade={onGrade}
              onUpgrade={onUpgrade}
            />
          ))}
          {hiddenPlayerCount > 0 && (
            <div
              style={{
                borderTop: '1px solid var(--border, #1A1A24)',
                padding: '10px 16px',
                textAlign: 'center',
              }}
            >
              <button
                type="button"
                onClick={() => setExpanded(true)}
                style={{
                  background: 'transparent',
                  border: 0,
                  cursor: 'pointer',
                  color: 'var(--grade-a, #00D4A0)',
                  fontSize: 12,
                  fontWeight: 600,
                }}
              >
                + {hiddenPlayerCount} more player{hiddenPlayerCount === 1 ? '' : 's'}
              </button>
            </div>
          )}
        </div>
      )}

      {/* Session 25 — streaks for players in THIS game, inline. The Slate
          matches streaks to games by team, so a card shows the streak
          context for the players actually on the floor/field. Renders
          only when there's at least one — no empty section. */}
      {streakRows.length > 0 && (
        <div
          style={{
            padding: '12px 20px 14px',
            borderTop: '1px solid var(--border, #1A1A24)',
            display: 'grid',
            gap: 9,
            background: 'linear-gradient(180deg, rgba(233,75,60,0.04) 0%, transparent 100%)',
          }}
        >
          <div
            className="mono"
            style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', color: 'var(--accent, #E94B3C)', textTransform: 'uppercase' }}
          >
            🔥 Streaks
          </div>
          {streakRows.map((s) => (
            <div
              key={`${s.player}-${s.description}`}
              style={{ display: 'flex', justifyContent: 'space-between', gap: 12, fontSize: 12.5, alignItems: 'baseline' }}
            >
              <span style={{ color: 'var(--text-0, #F0F0F5)', fontWeight: 700 }}>
                {s.player}
                {s.team ? <span style={{ color: 'var(--text-tertiary, #6B6B7B)', fontWeight: 400 }}> · {s.team}</span> : null}
              </span>
              <span style={{ color: 'var(--text-secondary, #8A8A9A)', textAlign: 'right' }}>{s.description}</span>
            </div>
          ))}
        </div>
      )}
    </article>
  );
}
