"""One chart: every arm of the study as an estimate with its 95 % interval,
against the +5 % bar the sealed rule set — and, at the bottom, what a world
with a real +5 % excess looks like through the same instrument, so a reader
can see that the study could have found one.

    python chart.py
writes chart.png here and site/public/images/quake-moon.svg.
Colours are the site's violet and amber against the dark ground. Identity is
carried by the row labels too, so colour is never the only cue.
"""

import json
import pathlib

import matplotlib
import numpy as np

matplotlib.use("Agg")
import matplotlib.pyplot as plt  # noqa: E402

import rule  # noqa: E402

HERE = pathlib.Path(__file__).parent
SITE_SVG = HERE.parents[1] / "site" / "public" / "images" / "quake-moon.svg"
GROUND, INK, DIM, GRID = "#0f1120", "#e8e3d8", "#a9a2c6", "#262a44"
MEASURED, REFERENCE = "#9085e9", "#c4861c"

res = json.loads((HERE / "results.json").read_text())
f_exp = res["f_exp"]

# the reference row: one simulated catalogue of the same size drawn from a
# world whose syzygy excess really is +5 %, through the same estimator.
rng = np.random.default_rng(rule.SEED + 7)
n = res["primary"]["n"]
inside = (rng.random(n) < f_exp * 1.05).astype(float)
f = inside.mean()
se = np.sqrt(f * (1 - f) / n) / f_exp
sim = (f / f_exp - 1.0, f / f_exp - 1.0 - 1.96 * se, f / f_exp - 1.0 + 1.96 * se)

rows = [
    ("a simulated +5 % world, same size", *sim, REFERENCE),
    ("M ≥ 7.0 only  (void: unfailable)", res["S2_m7"]["e"], res["S2_m7"]["lo"],
     res["S2_m7"]["hi"], MEASURED),
    ("full moon alone", res["S3_full"]["e"], res["S3_full"]["lo"],
     res["S3_full"]["hi"], MEASURED),
    ("shallow only, ≤ 70 km", res["S1_shallow"]["e"], res["S1_shallow"]["lo"],
     res["S1_shallow"]["hi"], MEASURED),
    ("all 7,327 events, clusters resampled", res["full"]["e"],
     res["full"]["lo"], res["full"]["hi"], MEASURED),
    ("5,368 mainshocks  (the verdict)", res["primary"]["e"],
     res["primary"]["lo"], res["primary"]["hi"], MEASURED),
]
rows = [(a, 100 * b, 100 * c, 100 * d, e) for a, b, c, d, e in rows]

plt.rcParams["font.family"] = "monospace"
plt.rcParams["svg.fonttype"] = "none"
fig, ax = plt.subplots(figsize=(9.2, 4.4), dpi=160)
fig.patch.set_facecolor(GROUND)
ax.set_facecolor(GROUND)

ax.axvline(0, color=DIM, linewidth=0.8, zorder=1)
ax.axvline(5, color=REFERENCE, linewidth=1.0, linestyle=(0, (4, 3)), zorder=1)
ax.text(5, len(rows) - 0.34, " +5 % bar", color=REFERENCE, fontsize=8,
        va="center", ha="left")

for i, (label, est, lo, hi, colour) in enumerate(rows):
    ax.plot([lo, hi], [i, i], color=colour, linewidth=2,
            solid_capstyle="round", zorder=2)
    ax.plot([est], [i], "o", color=colour, markersize=8,
            markeredgecolor=GROUND, markeredgewidth=2, zorder=3)
    ax.text(hi + 0.45, i, f"{est:+.2f} %", va="center", ha="left",
            color=INK, fontsize=8.5)

ax.set_yticks(range(len(rows)))
ax.set_yticklabels([r[0] for r in rows])
ax.set_xlim(-10.5, 12.5)
ax.set_ylim(-0.6, len(rows) - 0.25)
ax.set_xlabel("earthquakes near the full or new moon, against the time the "
              "moon spends there, % (95 % interval)", color=DIM, fontsize=9)
ax.tick_params(colors=DIM, labelsize=9, length=0)
ax.grid(True, axis="x", color=GRID, linewidth=0.6, zorder=0)
for side in ax.spines.values():
    side.set_visible(False)
ax.set_title("Earthquakes do not wait for the full moon",
             color=INK, fontsize=11, loc="left", pad=12)
ax.text(0, -0.26, "USGS ComCat, 7,327 events of M ≥ 6.0, 1973–2025 · rule "
        "sealed before the catalogue was fetched · "
        "untilnextsession.com/research/quake-moon/",
        transform=ax.transAxes, color=DIM, fontsize=7.2)

fig.tight_layout()
fig.savefig(HERE / "chart.png", facecolor=GROUND, bbox_inches="tight")
fig.savefig(SITE_SVG, facecolor=GROUND, bbox_inches="tight")
print("wrote chart.png and", SITE_SVG)
print("simulated +5 % row:", [round(100 * x, 2) for x in sim])
