#!/usr/bin/env python3
"""Derives the small JSON files the paper builder reads:
results/summary_minimal.json, results/exp3_mutation_table.json,
results/load_summary.json. Run after analyze.py."""
import json, os, re, statistics as st, sys
HERE = os.path.dirname(os.path.abspath(__file__)); RES = os.path.join(HERE, "..", "results")
sys.path.insert(0, HERE)


def jl(p):
    return [json.loads(l) for l in open(p) if l.strip().startswith("{")]


def pct(xs, p):
    xs = sorted(xs); k = (len(xs) - 1) * p / 100; lo, hi = int(k), min(int(k) + 1, len(xs) - 1)
    return xs[lo] + (xs[hi] - xs[lo]) * (k - lo)


def dist(xs):
    return {"n": len(xs), "p50": pct(xs, 50), "p95": pct(xs, 95), "min": min(xs), "max": max(xs)}


# Minimal reproduction
m = {"chain": {}}
for v in ("before", "after"):
    rows = jl(os.path.join(RES, f"min_lockscope_{v}.jsonl"))
    m[v] = {"same_tenant_ms": dist([r["same_tenant_ms"] for r in rows]), "other_tenant_ms": dist([r["other_tenant_ms"] for r in rows])}
    for kind in ("interleave", "stress"):
        t = [r for r in jl(os.path.join(RES, f"min_{kind}_{v}.jsonl")) if not r.get("summary")]
        m["chain"].setdefault(v, {})[kind] = {
            "trials": len(t), "chains_failing_verification": sum(not r["ok"] for r in t),
            "trials_with_shared_prev_hash": sum(r["shared_prev"] > 0 for r in t),
            "trials_with_writer_errors": sum(bool(r.get("writer_errors") or r.get("error")) for r in t)}
json.dump(m, open(os.path.join(RES, "summary_minimal.json"), "w"), indent=2)

# Mutation table
desc = {
    "M1_account_only_table_without_rls": "New table with only <code>account_id</code>, no RLS, SELECT granted to the request role",
    "M2_rls_on_granted_no_policy": "New <code>account_id</code> table, RLS on, granted, no policy",
    "M3_institution_table_without_rls": "New table with <code>institution_id</code>, no RLS, granted",
    "M4_force_rls_on_event": "<code>FORCE ROW LEVEL SECURITY</code> on <code>event</code>",
    "M5_audit_insert_policy": "An INSERT policy for the request role on <code>audit_event</code>",
    "M6_unkeyed_select_policy": "A <code>USING (true)</code> SELECT policy on <code>event</code>",
    "M7_person_scoped_table_without_rls": "New person-scoped table (<code>user_id</code> only), no RLS, granted",
    "M8_fk_derived_table_without_rls": "New child table (tenancy only via FK to <code>event</code>), no RLS, granted",
}
lines = [l.rstrip("\n").split("|", 2) for l in open(os.path.join(RES, "exp3_mutations.txt")) if not l.startswith("mutation|")]
tab = {}
for name, pred, v in lines:
    caught = not v.startswith("NONE")
    cell = "missed" if not caught else "caught: " + ", ".join(sorted({x.split("|")[0].split(" ")[0] for x in v.split(";")}))
    tab.setdefault(name, {})[pred] = cell
rows = [{"id": k.split("_")[0], "name": k, "desc": desc[k], "new": tab[k]["new"], "old": tab[k]["old"], "r1": tab[k]["scope-free"]} for k in desc]
json.dump(rows, open(os.path.join(RES, "exp3_mutation_table.json"), "w"), indent=2)

# Load averages during the timed runs
vals = []
for l in open(os.path.join(RES, "load.log")):
    mm = re.search(r"\{ ([\d.]+) ", l)
    if mm:
        vals.append(float(mm.group(1)))
json.dump({"samples": len(vals), "median": st.median(vals), "min": min(vals), "max": max(vals),
           "note": "one-minute load average, sampled every 30 s from 17:13Z (logging started after experiment 1)"},
          open(os.path.join(RES, "load_summary.json"), "w"), indent=2)
print(json.dumps(m["chain"], indent=1)); print(json.dumps(rows, indent=1))
