import json
import math
import time
from datetime import datetime
from pathlib import Path

import pytz
from nba_api.stats.endpoints import BoxScoreTraditionalV2, ScoreboardV2
from nba_api.stats.static import teams as nba_teams_static

CACHE_DIR = Path.home() / ".betlab" / "cache" / "nba"
ET = pytz.timezone("US/Eastern")

_TEAM_MAP: dict[int, str] | None = None


def _int(val) -> int:
    """Safe int conversion — handles None and NaN from pandas."""
    try:
        f = float(val)
        return 0 if math.isnan(f) else int(f)
    except (TypeError, ValueError):
        return 0


def _team_map() -> dict[int, str]:
    global _TEAM_MAP
    if _TEAM_MAP is None:
        _TEAM_MAP = {t["id"]: t["abbreviation"] for t in nba_teams_static.get_teams()}
    return _TEAM_MAP


def _cache_path(kind: str, key: str) -> Path:
    return CACHE_DIR / kind / f"{key}.json"


def _load_cache(kind: str, key: str, max_age_hours: float = 12) -> dict | list | None:
    p = _cache_path(kind, key)
    if not p.exists():
        return None
    if time.time() - p.stat().st_mtime > max_age_hours * 3600:
        return None
    return json.loads(p.read_text())


def _save_cache(kind: str, key: str, data: dict | list) -> None:
    p = _cache_path(kind, key)
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(json.dumps(data))


def _today_et() -> str:
    return datetime.now(ET).strftime("%Y-%m-%d")


def get_games_on_date(date: str) -> list[dict]:
    """Return [{game_id, team_home, team_away, status}] for given date."""
    max_age = 1.0 if date == _today_et() else 9999.0
    cached = _load_cache("scoreboard", date, max_age)
    if cached is not None:
        return cached  # type: ignore[return-value]

    board = ScoreboardV2(game_date=date)
    df = board.game_header.get_data_frame()
    tm = _team_map()

    games = [
        {
            "game_id": str(row["GAME_ID"]),
            "team_home": tm.get(int(row["HOME_TEAM_ID"]), str(row["HOME_TEAM_ID"])),
            "team_away": tm.get(int(row["VISITOR_TEAM_ID"]), str(row["VISITOR_TEAM_ID"])),
            "status": str(row["GAME_STATUS_TEXT"]).strip(),
        }
        for _, row in df.iterrows()
    ]
    _save_cache("scoreboard", date, games)
    return games


def get_box_score(game_id: str) -> dict:
    """Return {game_id, players: [...], teams: [...], final: bool}. Cached forever once final."""
    cached = _load_cache("boxscore", game_id, max_age_hours=9999)
    if cached and cached.get("final"):  # type: ignore[union-attr]
        return cached  # type: ignore[return-value]

    box = BoxScoreTraditionalV2(game_id=game_id)
    players_df = box.player_stats.get_data_frame()
    teams_df = box.team_stats.get_data_frame()

    players = []
    for _, r in players_df.iterrows():
        min_str = str(r.get("MIN") or "0:00")
        try:
            parts = min_str.split(":")
            minutes = int(parts[0]) + int(parts[1]) / 60 if len(parts) == 2 else 0.0
        except (ValueError, IndexError):
            minutes = 0.0

        players.append({
            "player_name": r["PLAYER_NAME"],
            "team": r["TEAM_ABBREVIATION"],
            "pts": _int(r["PTS"]),
            "reb": _int(r["REB"]),
            "ast": _int(r["AST"]),
            "stl": _int(r["STL"]),
            "blk": _int(r["BLK"]),
            "tov": _int(r["TO"]),
            "fg3m": _int(r["FG3M"]),
            "minutes": minutes,
            "dnp": minutes == 0,
        })

    teams = [
        {"team": r["TEAM_ABBREVIATION"], "pts": _int(r["PTS"])}
        for _, r in teams_df.iterrows()
    ]

    is_final = len(teams) == 2 and all(t["pts"] > 0 for t in teams)
    result = {"game_id": game_id, "players": players, "teams": teams, "final": is_final}
    _save_cache("boxscore", game_id, result)
    return result
