#!/usr/bin/env python3
"""DEEPFALL sweep analyzer — pure stdlib, no dependencies.

Reads the CSV/JSON written by `deepfall-sim` into `out/` and renders the headline
balance picture to the terminal: an ASCII Lineage x Calling heatmap, the strategy
band, the pacing/feel numbers, and the deep-content reachability. Runs anywhere
Python 3 does — no pandas/matplotlib needed.

    python3 analysis/analyze.py out
"""
import csv
import json
import sys
from pathlib import Path

LINEAGES = ["Dvergar", "Wisp", "Revenant", "Gnoll", "Sylphid", "GolemBorn"]
CALLINGS = ["Reaver", "Lorekeeper", "Wardancer", "Hierophant", "Warden", "Prospector"]

# Shading ramp from cold (weak) to hot (strong), keyed off winrate vs. the fair share.
RAMP = " .:-=+*#%@"


def shade(frac, lo=0.0, hi=0.45):
    t = max(0.0, min(1.0, (frac - lo) / (hi - lo)))
    return RAMP[min(len(RAMP) - 1, int(t * (len(RAMP) - 1)))]


def load_combo(out: Path):
    grid = {}
    with open(out / "combo_winrates.csv") as f:
        for row in csv.DictReader(f):
            grid[(row["lineage"], row["calling"])] = float(row["wins_frac"])
    return grid


def heatmap(grid):
    print("\nCOMBO WIN-RATE HEATMAP  (focal combo as Balanced vs. a mixed random field)")
    print("  rows = Lineage, cols = Calling.  Each cell: win% and a shade (fair share 25%).\n")
    colw = 11
    header = " " * 11 + "".join(c[:colw].ljust(colw) for c in CALLINGS)
    print(header)
    for l in LINEAGES:
        cells = []
        for c in CALLINGS:
            wr = grid.get((l, c), 0.0)
            cells.append(f"{shade(wr)*3} {wr*100:4.1f}%".ljust(colw))
        print(f"  {l:<9}" + "".join(cells))
    # Marginals.
    print()
    lmean = {l: sum(grid[(l, c)] for c in CALLINGS) / len(CALLINGS) for l in LINEAGES}
    cmean = {c: sum(grid[(l, c)] for l in LINEAGES) / len(LINEAGES) for c in CALLINGS}
    print("  Lineage averages : " + "  ".join(f"{l} {lmean[l]*100:4.1f}%" for l in LINEAGES))
    print("  Calling averages : " + "  ".join(f"{c} {cmean[c]*100:4.1f}%" for c in CALLINGS))
    vals = list(grid.values())
    print(f"\n  spread: {(max(vals)-min(vals))*100:.1f} pts   "
          f"best: {max(grid, key=grid.get)} {max(vals)*100:.1f}%   "
          f"worst: {min(grid, key=grid.get)} {min(vals)*100:.1f}%")


def bar(label, frac, width=44, scale=0.5):
    n = int(min(1.0, frac / scale) * width)
    return f"  {label:<11}{frac*100:5.1f}%  {'#'*n}"


def strategy(out: Path):
    print("\nSTRATEGY WIN-RATES  (fair share 25%)")
    with open(out / "strategy_winrates.csv") as f:
        for row in csv.DictReader(f):
            print(bar(row["strategy"], float(row["winrate"])))


def depth(out: Path):
    print("\nDEEP-CONTENT REACHABILITY  (claims per floor; deeper = richer + collapses first)")
    rows = list(csv.DictReader(open(out / "depth_claims.csv")))
    mx = max(int(r["claims"]) for r in rows) or 1
    for r in rows:
        n = int(int(r["claims"]) / mx * 40)
        print(f"  floor {r['depth']}  {'#'*n}  {r['claims']}")


def headline(out: Path):
    s = json.load(open(out / "summary.json"))
    print("\nHEADLINE")
    print(f"  seeds                : {s['seeds']}")
    print(f"  game length          : mean {s['mean_rounds']:.1f} rounds "
          f"({s['rounds_min']}-{s['rounds_max']})")
    print(f"  victory margin       : {s['mean_margin_frac']*100:.1f}% of winner (lower = tighter)")
    print(f"  feel-bad / game      : {s['feelbad_per_game']:.3f} (Taken w/o Last Stand)")
    print(f"  deep claims by R1    : {s['deep_claim_drafted_round_share']*100:.1f}% "
          f"(low = deep game is NOT decided at game start)")
    print(f"  dominant strategy    : {'YES' if s['strategy_dominant'] else 'no'}")
    print(f"  combo spread         : {s['combo_spread']*100:.1f} pts")


def main():
    out = Path(sys.argv[1] if len(sys.argv) > 1 else "out")
    if not (out / "combo_winrates.csv").exists():
        sys.exit(f"no sweep data in {out}/ — run: cargo run -p deepfall-sim -- --out {out}")
    print("=" * 78)
    print("DEEPFALL — balance sweep analysis")
    print("=" * 78)
    headline(out)
    strategy(out)
    heatmap(load_combo(out))
    depth(out)
    print("\n" + "=" * 78)


if __name__ == "__main__":
    main()
