"""Render the article's figure and CSVs from its original Robustness UI export.

Run beside all-strategies-result.json using Python with pandas and matplotlib.
This script analyzes saved results; it does not rerun the simulation.
Produced with Python 3.10.11, pandas 2.3.3, and matplotlib 3.10.8.
"""

from __future__ import annotations

import json
from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.colors import Normalize
from matplotlib.ticker import PercentFormatter


def main() -> None:
    folder = Path(__file__).resolve().parent
    result = json.loads((folder / "all-strategies-result.json").read_text(encoding="utf-8"))
    config = result["config"]
    names = config["strategy_names"]
    rows = pd.DataFrame(result["seed_results"])
    seeds = [config["start_seed"] + i * config["seed_step"] for i in range(config["seed_count"])]
    assert len(names) == len(set(names)) == 60
    assert len(rows) == 60 * len(seeds) == 600
    assert not rows.duplicated(["seed", "strategy"]).any()
    assert set(rows["strategy"]) == set(names)
    assert set(rows["seed"]) == set(seeds)
    assert (rows["rounds"] == 5900).all()

    rows["first_place"] = rows["rank"] == 1
    summary = rows.groupby("strategy", as_index=False).agg(
        mean_payoff=("points_per_round", "mean"),
        min_payoff=("points_per_round", "min"),
        max_payoff=("points_per_round", "max"),
        payoff_std=("points_per_round", lambda values: values.std(ddof=0)),
        mean_cooperation=("cooperation_rate", "mean"),
        mean_seed_rank=("rank", "mean"),
        best_seed_rank=("rank", "min"),
        worst_seed_rank=("rank", "max"),
        first_place_seeds=("first_place", "sum"),
    )
    summary["rank_by_mean_payoff"] = summary["mean_payoff"].rank(method="min", ascending=False).astype(int)
    summary = summary.sort_values(["mean_payoff", "strategy"], ascending=[False, True]).reset_index(drop=True)
    columns = ["rank_by_mean_payoff"] + [col for col in summary if col != "rank_by_mean_payoff"]
    summary[columns].to_csv(folder / "all-strategies-summary.csv", index=False)
    rows.drop(columns="first_place").to_csv(folder / "all-strategies-seeds.csv", index=False)

    paper, ink, muted, grid = "#101414", "#edf4ef", "#b2c2ba", "#2a3931"
    plt.rcParams.update({"font.family": "DejaVu Sans", "text.color": ink, "axes.labelcolor": muted})
    fig, axes = plt.subplots(1, 2, figsize=(18, 14), facecolor=paper)
    fig.subplots_adjust(left=0.19, right=0.94, top=0.82, bottom=0.18, wspace=0.88)
    cmap, norm = plt.get_cmap("viridis"), Normalize(0, 1)

    for panel, ax in enumerate(axes):
        subset = summary.iloc[panel * 30 : (panel + 1) * 30]
        y = list(range(len(subset)))
        ax.set_facecolor(paper)
        ax.hlines(y, subset["min_payoff"], subset["max_payoff"], color="#95aaa0", linewidth=2.3, zorder=2)
        ax.scatter(subset["mean_payoff"], y, c=subset["mean_cooperation"], cmap=cmap, norm=norm, s=100, zorder=3)
        labels = [f"{int(row.rank_by_mean_payoff):02d}  {row.strategy}" for row in subset.itertuples()]
        ax.set_yticks(y, labels=labels, fontsize=16)
        ax.set_ylim(29.8, -1)
        ax.set_xlim(1.65, 2.64)
        ax.set_xticks([1.75, 2.0, 2.25, 2.5])
        ax.set_xlabel("Points per round", fontsize=15, labelpad=14)
        ax.tick_params(axis="x", colors=muted, labelsize=14, length=0, pad=12)
        ax.tick_params(axis="y", colors=ink, length=0, pad=12)
        ax.grid(axis="x", color=grid, linewidth=1)
        ax.set_axisbelow(True)
        for spine in ax.spines.values():
            spine.set_visible(False)
        ax.set_title(f"Ranks {panel * 30 + 1} to {(panel + 1) * 30}", loc="left", color=muted, fontsize=16, pad=24)
        for index, row in enumerate(subset.itertuples()):
            ax.text(1.035, index, f"{row.mean_payoff:.3f}", transform=ax.get_yaxis_transform(), va="center", fontsize=15, color=ink)

    fig.text(0.04, 0.95, "All 60 strategies in one field", fontsize=30, weight="bold", va="top")
    fig.text(0.04, 0.905, "Ranked by mean payoff across ten seeds", fontsize=20, color=muted)
    fig.text(0.04, 0.867, "Dots: mean payoff. Lines: lowest to highest observed seed payoff.", fontsize=15, color=muted)
    color_axis = fig.add_axes([0.33, 0.105, 0.34, 0.014])
    colorbar = fig.colorbar(plt.cm.ScalarMappable(norm=norm, cmap=cmap), cax=color_axis, orientation="horizontal")
    colorbar.set_ticks([0, 0.5, 1])
    colorbar.ax.xaxis.set_major_formatter(PercentFormatter(1))
    colorbar.ax.tick_params(colors=muted, labelsize=13, length=0, pad=7)
    colorbar.set_label("Mean cooperation", color=muted, fontsize=14, labelpad=8)
    colorbar.outline.set_visible(False)
    fig.text(0.04, 0.043, "100 rounds per match | 1 repetition per seed | seeds 0 to 9", fontsize=14, color=muted)
    fig.text(0.04, 0.018, "No self-play or execution errors; match length undisclosed. Ranges are descriptive, not confidence intervals.", fontsize=12, color=muted)
    fig.savefig(folder / "all-strategies-ranking.svg", facecolor=paper, metadata={"Date": None})
    svg = folder / "all-strategies-ranking.svg"
    svg.write_text("\n".join(line.rstrip() for line in svg.read_text(encoding="utf-8").splitlines()) + "\n", encoding="utf-8")
    fig.savefig(folder / "all-strategies-ranking.png", dpi=150, facecolor=paper)
    plt.close(fig)
    print(summary[columns].head(12).to_string(index=False))
    print(f"Wrote figure and CSVs for {len(summary)} strategies and {len(rows)} seed records.")


if __name__ == "__main__":
    main()
