'use client';

import { useState } from 'react';
import type { PropRowProp, PropRowResult, Tier } from '@/components/PropRow';
// 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_EMOJI: Record<SlateSport, string> = {
  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';

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;
}

// 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();
}

const SPORT_LABEL: Record<SlateSport, string> = {
  nba: 'NBA',
  wnba: 'WNBA',
  mlb: 'MLB',
  soccer: 'SOCCER',
};

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,
  } = props;
  const [expanded, setExpanded] = useState(false);
  const badge = statusBadge(status, score);
  const bookRows = gameLines?.books ? Object.entries(gameLines.books) : [];

  // 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: '14px 16px',
          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. */}
          <div
            style={{
              display: 'flex',
              alignItems: 'center',
              gap: 10,
              fontSize: 16,
              fontWeight: 700,
              color: 'var(--text-0, #F0F0F5)',
              letterSpacing: '-0.01em',
            }}
          >
            <span aria-hidden style={{ fontSize: 14 }}>{SPORT_EMOJI[sport]}</span>
            <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>
            <span
              className="mono"
              style={{
                marginLeft: 'auto',
                fontSize: 10,
                fontWeight: 700,
                color: '#0A0A0F',
                background: accent,
                padding: '3px 8px',
                borderRadius: 4,
                letterSpacing: '0.08em',
              }}
              aria-label={`Sport: ${SPORT_LABEL[sport]}`}
            >
              {SPORT_LABEL[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. */}
      {bookRows.length > 0 && (
        <div
          style={{
            padding: '10px 16px',
            borderTop: '1px solid var(--border, #1A1A24)',
            display: 'grid',
            gap: 6,
            background: 'rgba(255,255,255,0.015)',
          }}
        >
          <div
            className="mono"
            style={{ fontSize: 10, letterSpacing: '0.08em', color: 'var(--text-tertiary, #6B6B7B)', textTransform: 'uppercase' }}
          >
            Game Lines
          </div>
          {bookRows.map(([book, line]) => (
            <div
              key={book}
              className="mono"
              style={{ display: 'flex', gap: 12, fontSize: 11, color: 'var(--text-secondary, #8A8A9A)', flexWrap: 'wrap' }}
            >
              <span style={{ minWidth: 64, color: 'var(--text-0, #F0F0F5)', fontWeight: 700, textTransform: 'capitalize' }}>{book}</span>
              {line.awayML && <span>{teamAbbr(awayTeam, sport)} {line.awayML}</span>}
              {line.homeML && <span>{teamAbbr(homeTeam, sport)} {line.homeML}</span>}
              {line.total && <span>O/U {line.total}</span>}
            </div>
          ))}
        </div>
      )}

      {propList.length === 0 ? (
        <p
          style={{
            padding: '20px 16px',
            color: 'var(--text-tertiary, #6B6B7B)',
            fontSize: 13,
            textAlign: 'center',
          }}
        >
          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>
      )}
    </article>
  );
}
