#!/usr/bin/env python3
"""Enforcement in the Dark — analysis script. Single source of every
statistic. Reads landscape.json (54 countries) and decisions.json; writes
figures.json, charts, and renders enforcement-in-the-dark.md from the
.src.md template.

Usage:  python3 analyze.py
        /tmp/env/bin/python analyze.py --charts
"""

import json
import sys
from collections import Counter
from datetime import date
from pathlib import Path

HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent))
from lib_report import fmt_int, pct, render_template  # noqa: E402

DATA = HERE.parent.parent / "data"
CHARTS = HERE / "charts"
BRAND_STYLE = HERE.parent.parent.parent.parent / "brand" / "charts" / "lawlab.mplstyle"
TEMPLATE = HERE / "enforcement-in-the-dark.src.md"
REPORT = HERE / "enforcement-in-the-dark.md"

LANG = {"fr": "French", "en": "English", "pt": "Portuguese"}


def load():
    landscape = json.loads((DATA / "landscape.json").read_text())
    decisions = json.loads((DATA / "decisions.json").read_text())
    return landscape, decisions


def tiers(landscape):
    """Strict partition of all 54 states, in funnel order."""
    a, b, c, d, e = [], [], [], [], []
    for x in landscape:
        law = (x.get("dp_law") or {}).get("status") == "in_force"
        op = (x.get("regulator") or {}).get("operational")
        if x.get("decisions_published"):
            a.append(x)
        elif x.get("enforcement_active"):
            b.append(x)
        elif op:
            c.append(x)
        elif law:
            d.append(x)
        else:
            e.append(x)
    return a, b, c, d, e


def names(rows):
    return ", ".join(sorted(x["country"] for x in rows))


def compute(landscape, decisions):
    f = {}
    today = date.today().isoformat()
    f["build_date"] = today
    f["build_year"] = today[:4]

    a, b, c, d, e = tiers(landscape)
    assert len(a) + len(b) + len(c) + len(d) + len(e) == len(landscape)

    f["n_countries"] = fmt_int(len(landscape))
    f["n_laws_inforce"] = fmt_int(sum(
        1 for x in landscape
        if (x.get("dp_law") or {}).get("status") == "in_force"))
    f["n_operational"] = fmt_int(sum(
        1 for x in landscape if (x.get("regulator") or {}).get("operational")))
    f["n_enforcing"] = fmt_int(sum(
        1 for x in landscape if x.get("enforcement_active")))
    f["n_publishing"] = fmt_int(len(a))

    for key, rows in (("a", a), ("b", b), ("c", c), ("d", d), ("e", e)):
        f[f"n_tier_{key}"] = fmt_int(len(rows))
        f[f"tier_{key}_countries"] = names(rows)

    # scorecard: the five-tier table
    tier_meta = [
        ("A", "Publishes decisions", a),
        ("B", "Enforces without publishing", b),
        ("C", "Operational regulator, no observable enforcement", c),
        ("D", "Law in force, regulator not yet operational", d),
        ("E", "No comprehensive law in force", e),
    ]
    f["scorecard_table"] = "\n".join(
        f"| {g} | {label} | {len(rows)} | {names(rows)} |"
        for g, label, rows in tier_meta)

    # the 21 enforcing states in detail
    rows = []
    for x in sorted(a + b, key=lambda x: (not x.get("decisions_published"),
                                          x["country"])):
        pub = "Yes" if x.get("decisions_published") else "**No**"
        lang = LANG.get(x.get("publication_language"), "n/a") \
            if x.get("decisions_published") else "n/a"
        fmt = x.get("publication_format") or "n/a" \
            if x.get("decisions_published") else "n/a"
        vol = x.get("estimated_decision_volume") or "unknown"
        rows.append(f"| {x['country']} | {x['region']} | {pub} | {lang} | "
                    f"{fmt} | {vol} |")
    f["enforcing_table"] = "\n".join(rows)

    # language split among publishers
    langs = Counter(x.get("publication_language") for x in a)
    f["n_pub_fr"] = fmt_int(langs.get("fr", 0))
    f["n_pub_en"] = fmt_int(langs.get("en", 0))
    f["n_pub_pt"] = fmt_int(langs.get("pt", 0))
    f["n_pub_non_en"] = fmt_int(len(a) - langs.get("en", 0))
    f["pct_pub_non_en"] = pct(len(a) - langs.get("en", 0), len(a))
    f["fr_publishers"] = names(
        [x for x in a if x.get("publication_language") == "fr"])
    f["en_publishers"] = names(
        [x for x in a if x.get("publication_language") == "en"])

    # regional pattern
    regions = ["North Africa", "West Africa", "Central Africa",
               "East Africa", "Southern Africa"]
    rows = []
    for r in regions:
        cs = [x for x in landscape if x["region"] == r]
        rows.append("| {} | {} | {} | {} |".format(
            r, len(cs),
            sum(1 for x in cs if x.get("enforcement_active")),
            sum(1 for x in cs if x.get("decisions_published"))))
    f["region_table"] = "\n".join(rows)
    central = [x for x in landscape if x["region"] == "Central Africa"]
    f["n_central"] = fmt_int(len(central))
    f["n_central_laws"] = fmt_int(sum(
        1 for x in central
        if (x.get("dp_law") or {}).get("status") == "in_force"))
    f["n_central_publishing"] = fmt_int(sum(
        1 for x in central if x.get("decisions_published")))

    # what the tracker actually holds, for the "cost" chapter
    f["n_records"] = fmt_int(len(decisions))
    juris = Counter(x["country"] for x in decisions)
    f["n_tracked_jurisdictions"] = fmt_int(len(juris))
    f["n_records_kenya"] = fmt_int(juris.get("Kenya", 0))

    # dark-five population-free stakes: laws with criminal exposure, unpublished
    dark = {x["country"]: x for x in b}
    f["dark_five"] = names(b)
    return f


def build_charts(landscape):
    import matplotlib
    matplotlib.use("Agg")
    import matplotlib.pyplot as plt
    if BRAND_STYLE.exists():
        plt.style.use(str(BRAND_STYLE))
    CHARTS.mkdir(exist_ok=True)
    cobalt = "#2168F2"

    def save(fig, name):
        for ext in ("svg", "png"):
            fig.savefig(CHARTS / f"{name}.{ext}", bbox_inches="tight",
                        facecolor="white")
        plt.close(fig)
        print(f"  chart: {name}.svg/.png")

    # 1 — the funnel
    steps = [
        ("AU member states", len(landscape)),
        ("DP law in force", sum(
            1 for x in landscape
            if (x.get("dp_law") or {}).get("status") == "in_force")),
        ("Operational regulator", sum(
            1 for x in landscape
            if (x.get("regulator") or {}).get("operational"))),
        ("Actively enforcing", sum(
            1 for x in landscape if x.get("enforcement_active"))),
        ("Publishing decisions", sum(
            1 for x in landscape if x.get("decisions_published"))),
    ]
    fig, ax = plt.subplots(figsize=(8, 3.6))
    nm = [s[0] for s in steps][::-1]
    vals = [s[1] for s in steps][::-1]
    shades = [cobalt, "#3F7DF4", "#6D9DF7", "#9CBEFA", "#CADEFC"][::-1]
    ax.barh(nm, vals, color=shades, height=0.6)
    for i, v in enumerate(vals):
        ax.text(v + 0.7, i, str(v), va="center", fontsize=10, color="#1A1A1A")
    ax.set_title("The enforcement–publication funnel, 54 African Union states, mid-2026")
    ax.set_xlim(0, 60)
    fig.text(0.12, -0.05,
             "Source: landscape.json (54/54 countries verified with per-cell "
             "source URLs), Law Lab Africa Research.",
             fontsize=7.5, color="#8A93A6")
    save(fig, "fig1-funnel")

    # 2 — who publishes, in which language
    a = [x for x in landscape if x.get("decisions_published")]
    langs = Counter(x.get("publication_language") for x in a)
    pairs = [("French", langs.get("fr", 0)), ("English", langs.get("en", 0)),
             ("Portuguese", langs.get("pt", 0))]
    fig, ax = plt.subplots(figsize=(7, 2.6))
    nm = [p[0] for p in pairs][::-1]
    vals = [p[1] for p in pairs][::-1]
    ax.barh(nm, vals, color=[cobalt, cobalt, "#C9962D"][::-1], height=0.55)
    for i, v in enumerate(vals):
        ax.text(v + 0.15, i, str(v), va="center", fontsize=10, color="#1A1A1A")
    n_pub = len(a)
    n_en = langs.get("en", 0)
    ax.set_title(
        f"The {n_pub} publishing regulators, by publication language")
    ax.set_xlim(0, max(vals) * 1.2)
    fig.text(0.12, -0.09,
             f"English-language coverage of African enforcement reads {n_en} "
             f"of {n_pub} venues. Source: landscape.json.",
             fontsize=7.5, color="#8A93A6")
    save(fig, "fig2-languages")


def main():
    landscape, decisions = load()
    figures = compute(landscape, decisions)
    (HERE / "figures.json").write_text(
        json.dumps(figures, indent=1, ensure_ascii=False) + "\n")
    print(f"figures.json: {len(figures)} figures")
    if "--charts" in sys.argv:
        build_charts(landscape)
    if TEMPLATE.exists():
        render_template(TEMPLATE, REPORT, figures)
    else:
        print("template missing; skipping render")


if __name__ == "__main__":
    main()
