import difflib

from ...core.db import get_conn
from ...core.journal import settle_bet
from .client import get_box_score, get_games_on_date

_STAT_FIELDS = {
    "PTS": "pts", "REB": "reb", "AST": "ast",
    "STL": "stl", "BLK": "blk", "TOV": "tov", "FG3M": "fg3m",
}
_COMBO_STATS = {"PRA", "PR", "PA", "RA"}


def _get_stat(player: dict, stat_type: str) -> int | None:
    if stat_type == "PRA":
        return player["pts"] + player["reb"] + player["ast"]
    if stat_type == "PR":
        return player["pts"] + player["reb"]
    if stat_type == "PA":
        return player["pts"] + player["ast"]
    if stat_type == "RA":
        return player["reb"] + player["ast"]
    field = _STAT_FIELDS.get(stat_type)
    return player[field] if field else None


def _match_player(name: str, players: list[dict]) -> dict | None:
    names = [p["player_name"] for p in players]
    matches = difflib.get_close_matches(name, names, n=1, cutoff=0.6)
    if not matches:
        return None
    return next(p for p in players if p["player_name"] == matches[0])


def _grade_prop(bet: dict, box: dict) -> tuple[str, float]:
    player = _match_player(bet["player_name"], box["players"])
    if not player:
        return "void", bet["stake"]
    if player["dnp"]:
        return "void", bet["stake"]

    actual = _get_stat(player, bet["stat_type"])
    if actual is None:
        return "void", bet["stake"]

    line = bet["line"]
    if actual > line:
        won = bet["direction"] == "over"
    elif actual < line:
        won = bet["direction"] == "under"
    else:
        return "push", bet["stake"]

    return ("won", round(bet["stake"] * bet["odds_taken"], 2)) if won else ("lost", 0.0)


def _grade_game(bet: dict, box: dict) -> tuple[str, float]:
    if len(box["teams"]) < 2:
        return "void", bet["stake"]

    home = next((t for t in box["teams"] if t["team"] == bet["team_home"]), None)
    away = next((t for t in box["teams"] if t["team"] == bet["team_away"]), None)
    if not home or not away:
        return "void", bet["stake"]

    ph, pa = home["pts"], away["pts"]
    market = bet["market"]
    line = bet.get("line") or 0.0

    if market == "total_over":
        result, push = (ph + pa) > line, (ph + pa) == line
    elif market == "total_under":
        result, push = (ph + pa) < line, (ph + pa) == line
    elif market == "spread_home":
        diff = ph - pa
        result, push = diff > line, diff == line
    elif market == "spread_away":
        diff = pa - ph
        result, push = diff > line, diff == line
    elif market == "moneyline_home":
        result, push = ph > pa, ph == pa
    elif market == "moneyline_away":
        result, push = pa > ph, pa == ph
    else:
        return "void", bet["stake"]

    if push:
        return "push", bet["stake"]
    return ("won", round(bet["stake"] * bet["odds_taken"], 2)) if result else ("lost", 0.0)


def settle_date(date: str, dry_run: bool = False) -> list[dict]:
    """Grade all open NBA bets for game_date. Returns list of result dicts."""
    with get_conn() as conn:
        open_bets = [
            dict(r) for r in conn.execute(
                "SELECT * FROM bets WHERE status='open' AND game_date=? AND sport='nba'",
                (date,),
            ).fetchall()
        ]

    if not open_bets:
        return []

    games = get_games_on_date(date)
    game_id_map = {(g["team_home"], g["team_away"]): g["game_id"] for g in games}
    box_cache: dict[str, dict] = {}
    results = []

    for bet in open_bets:
        game_id = bet.get("game_id") or game_id_map.get((bet["team_home"], bet["team_away"]))
        if not game_id:
            results.append({**bet, "_action": "skip", "_reason": "game not found"})
            continue

        if game_id not in box_cache:
            try:
                box_cache[game_id] = get_box_score(game_id)
            except Exception as e:
                results.append({**bet, "_action": "skip", "_reason": str(e)})
                continue

        box = box_cache[game_id]
        if not box.get("final"):
            results.append({**bet, "_action": "skip", "_reason": "not final yet"})
            continue

        is_prop = bet.get("stat_type") is not None
        status, payout = _grade_prop(bet, box) if is_prop else _grade_game(bet, box)

        if not dry_run:
            settle_bet(bet["id"], status, payout)

        results.append({**bet, "_action": "settled", "_status": status, "_payout": payout})

    return results
