"""
analytics.py

SQB session analytics: map win rates, team composition analysis,
player consistency, time-of-day performance, and opponent difficulty.
"""

# Standard Library Imports
import json
import logging
import math
import re
from collections import Counter, defaultdict
from datetime import datetime, timezone

# Third-Party Library Imports
import aiosqlite

# Local Module Imports
from data_parser import get_unit_type_abbrev
from .utils import SQ_BATTLES_DB_PATH, decompress_json


async def get_map_stats(squadron_short: str, start_ts: int = 0) -> list[dict]:
    """Win rate by map for a squadron.

    Returns list of dicts sorted by total games desc:
        [{map_name, wins, losses, total, win_rate}, ...]
    """
    async with aiosqlite.connect(SQ_BATTLES_DB_PATH, timeout=30.0) as db:
        await db.execute("PRAGMA busy_timeout=30000;")
        rows = await db.execute_fetchall(
            """SELECT map_name, winning_sq, losing_sq
               FROM match_summary
               WHERE (winning_sq = ? OR losing_sq = ?)
                 AND endtime_unix >= ?""",
            (squadron_short, squadron_short, start_ts),
        )

    seen: dict[str, str] = {}  # lowercase key -> first clean spelling

    def _normalize_map(raw: str) -> tuple[str, str]:
        clean = re.sub(r"^\s*\[[^\]]+\]\s*", "", raw).strip() or "Unknown"
        key = clean.lower()
        if key not in seen:
            seen[key] = clean
        return key, seen[key]

    stats: dict[str, dict] = {}
    for raw_name, winning_sq, losing_sq in rows:
        key, name = _normalize_map(raw_name or "Unknown")
        entry = stats.setdefault(key, {"map_name": name, "wins": 0, "losses": 0})
        if winning_sq == squadron_short:
            entry["wins"] += 1
        else:
            entry["losses"] += 1

    result = list(stats.values())
    for r in result:
        r["total"] = r["wins"] + r["losses"]
        r["win_rate"] = round(r["wins"] / r["total"] * 100, 1) if r["total"] else 0
    result.sort(key=lambda x: x["total"], reverse=True)
    return result


async def get_comp_analysis(squadron_short: str, start_ts: int = 0) -> list[dict]:
    """Vehicle type composition vs win rate.

    Parses winning_team_json/losing_team_json, classifies vehicles,
    builds comp signatures (e.g. "3T 1H 1F"), correlates with outcomes.

    Returns list sorted by usage:
        [{comp_signature, wins, losses, total, win_rate}, ...]
    """
    async with aiosqlite.connect(SQ_BATTLES_DB_PATH, timeout=30.0) as db:
        await db.execute("PRAGMA busy_timeout=30000;")
        rows = await db.execute_fetchall(
            """SELECT winning_sq, losing_sq, winning_team_json, losing_team_json
               FROM match_summary
               WHERE (winning_sq = ? OR losing_sq = ?)
                 AND endtime_unix >= ?""",
            (squadron_short, squadron_short, start_ts),
        )

    stats: dict[str, dict] = {}
    for winning_sq, losing_sq, w_json, l_json in rows:
        is_win = winning_sq == squadron_short
        team_json = w_json if is_win else l_json
        if not team_json:
            continue

        try:
            team = decompress_json(team_json)
        except (json.JSONDecodeError, TypeError, OSError):
            continue

        # Classify vehicles
        type_counts: Counter = Counter()
        players = team.get("players", [])
        for p in players:
            veh_internal = p.get("vehicle", "")
            if not veh_internal or veh_internal == "DISCONNECTED":
                continue
            vtype = get_unit_type_abbrev(veh_internal)
            type_counts[vtype] += 1

        if not type_counts:
            continue

        # Build signature: sorted by count desc, then alpha
        sig_parts = sorted(type_counts.items(), key=lambda x: (-x[1], x[0]))
        signature = " ".join(f"{count}{abbr}" for abbr, count in sig_parts)

        entry = stats.setdefault(signature, {"comp_signature": signature, "wins": 0, "losses": 0})
        if is_win:
            entry["wins"] += 1
        else:
            entry["losses"] += 1

    result = list(stats.values())
    for r in result:
        r["total"] = r["wins"] + r["losses"]
        r["win_rate"] = round(r["wins"] / r["total"] * 100, 1) if r["total"] else 0
    result.sort(key=lambda x: x["total"], reverse=True)
    return result[:20]


async def get_player_consistency(squadron_short: str, min_games: int = 50) -> list[dict]:
    """Per-player performance variance.

    Returns list sorted by most recently played:
        [{uid, nick, avg_kills, std_kills, avg_deaths, std_deaths, games, consistency_score, last_played}, ...]
    """
    async with aiosqlite.connect(SQ_BATTLES_DB_PATH, timeout=30.0) as db:
        await db.execute("PRAGMA busy_timeout=30000;")
        # squadron_name in player_games_hist stores the short name (e.g. "DSPL")
        rows = await db.execute_fetchall(
            """SELECT UID, nick,
                      SUM(ground_kills + air_kills) as total_kills,
                      SUM(deaths) as total_deaths,
                      session_id,
                      MAX(endtime_unix) as last_played
               FROM player_games_hist
               WHERE squadron_name = ?
               GROUP BY UID, session_id""",
            (squadron_short,),
        )

    # Aggregate per-player
    player_data: dict[str, dict] = {}
    for uid, nick, kills, deaths, session_id, last_played in rows:
        entry = player_data.setdefault(uid, {"uid": uid, "nick": nick, "kills": [], "deaths": [], "last_played": 0})
        entry["nick"] = nick  # keep latest nick
        entry["kills"].append(kills or 0)
        entry["deaths"].append(deaths or 0)
        entry["last_played"] = max(entry["last_played"], last_played or 0)

    result = []
    for uid, data in player_data.items():
        games = len(data["kills"])
        if games < min_games:
            continue

        avg_kills = sum(data["kills"]) / games
        avg_deaths = sum(data["deaths"]) / games

        std_kills = math.sqrt(sum((k - avg_kills) ** 2 for k in data["kills"]) / games)
        std_deaths = math.sqrt(sum((d - avg_deaths) ** 2 for d in data["deaths"]) / games)

        # Consistency score: lower is more consistent (normalized std dev)
        consistency = (std_kills + std_deaths) / max(avg_kills + avg_deaths, 1)

        result.append({
            "uid": uid,
            "nick": data["nick"],
            "avg_kills": round(avg_kills, 2),
            "std_kills": round(std_kills, 2),
            "avg_deaths": round(avg_deaths, 2),
            "std_deaths": round(std_deaths, 2),
            "games": games,
            "consistency_score": round(consistency, 3),
            "last_played": data["last_played"],
        })

    result.sort(key=lambda x: x["avg_kills"] / max(x["avg_deaths"], 0.01), reverse=True)
    return result[:128]


async def get_matchup_history(squadron_short: str, start_ts: int = 0) -> dict:
    """Top opponents this squadron has won against and lost to.

    Returns:
        {
            "won_against":  [{opponent, wins, losses, total, win_rate}, ...up to 10],
            "lost_against": [{opponent, wins, losses, total, win_rate}, ...up to 10],
            "total_opponents": int,
        }
    """
    async with aiosqlite.connect(SQ_BATTLES_DB_PATH, timeout=30.0) as db:
        await db.execute("PRAGMA busy_timeout=30000;")
        rows = await db.execute_fetchall(
            """SELECT winning_sq, losing_sq
               FROM match_summary
               WHERE (winning_sq = ? OR losing_sq = ?)
                 AND endtime_unix >= ?""",
            (squadron_short, squadron_short, start_ts),
        )

    stats: dict[str, dict] = {}
    for winning_sq, losing_sq in rows:
        if not winning_sq or not losing_sq or winning_sq == losing_sq:
            continue
        opponent = losing_sq if winning_sq == squadron_short else winning_sq
        if not opponent:
            continue
        entry = stats.setdefault(opponent, {"opponent": opponent, "wins": 0, "losses": 0})
        if winning_sq == squadron_short:
            entry["wins"] += 1
        else:
            entry["losses"] += 1

    enriched = []
    for r in stats.values():
        r["total"] = r["wins"] + r["losses"]
        r["win_rate"] = round(r["wins"] / r["total"] * 100, 1) if r["total"] else 0
        enriched.append(r)

    won_against = sorted(enriched, key=lambda x: (x["wins"], x["total"]), reverse=True)[:10]
    lost_against = sorted(enriched, key=lambda x: (x["losses"], x["total"]), reverse=True)[:10]

    return {
        "won_against": won_against,
        "lost_against": lost_against,
        "total_opponents": len(enriched),
    }


async def get_time_performance(squadron_short: str) -> dict:
    """Win rate bucketed by hour of day (UTC).

    Returns dict keyed by hour (0-23):
        {hour: {wins, losses, total, win_rate}, ...}
    """
    async with aiosqlite.connect(SQ_BATTLES_DB_PATH, timeout=30.0) as db:
        await db.execute("PRAGMA busy_timeout=30000;")
        rows = await db.execute_fetchall(
            """SELECT endtime_unix, winning_sq, losing_sq
               FROM match_summary
               WHERE (winning_sq = ? OR losing_sq = ?)
                 AND endtime_unix > 0""",
            (squadron_short, squadron_short),
        )

    stats: dict[int, dict] = {}
    for endtime_unix, winning_sq, losing_sq in rows:
        hour = datetime.fromtimestamp(endtime_unix, tz=timezone.utc).hour
        entry = stats.setdefault(hour, {"wins": 0, "losses": 0})
        if winning_sq == squadron_short:
            entry["wins"] += 1
        else:
            entry["losses"] += 1

    result = {}
    for hour in sorted(stats.keys()):
        e = stats[hour]
        total = e["wins"] + e["losses"]
        result[hour] = {
            "wins": e["wins"],
            "losses": e["losses"],
            "total": total,
            "win_rate": round(e["wins"] / total * 100, 1) if total else 0,
        }
    return result
