"""Score the rule against the rows fetched, and write results.txt.

    python research/so-accepted-answer/analyse.py

Reads ~/data/so-accepted/<cohort>.jsonl, applies `rule.eligible`, and prints
the verdict for each cohort with its positive control and the registered
secondary numbers. Nothing here chooses a threshold: every bar comes from
rule.py, which was sealed before the first request.

This rewrites results.txt whole, so any note added to that file by hand is
lost on a re-run — keep hand notes in the entry, not here.
"""

import json
import os
import random
import statistics
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import rule  # noqa: E402

DATA = os.path.expanduser("~/data/so-accepted")
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "results.txt")


def load(cohort):
    path = os.path.join(DATA, cohort + ".jsonl")
    with open(path) as fh:
        return [json.loads(line) for line in fh]


def report(cohort, lines):
    rows = load(cohort)
    dropped = {}
    good = []
    for q in rows:
        why = rule.eligible(q)
        if why:
            dropped[why] = dropped.get(why, 0) + 1
        else:
            good.append(q)

    n = len(good)
    k = sum(1 for q in good if rule.not_top(q))
    v = rule.verdict(k, n)

    rng = random.Random(rule.CONTROL_SEED)
    ck = sum(1 for q in good if rule.control_not_top(q, rng))
    cf = ck / n if n else 0.0
    control_ok = cf - v["f"] >= rule.CONTROL_MARGIN

    ties = sum(1 for q in good
               if rule.score_of_accepted(q) == max(a["score"] for a in q["answers"])
               and sum(1 for a in q["answers"]
                       if a["score"] == rule.score_of_accepted(q)) > 1)
    gaps, later = [], 0
    for q in good:
        if not rule.not_top(q):
            continue
        acc = [a for a in q["answers"] if a.get("is_accepted")][0]
        top = max(q["answers"], key=lambda a: a["score"])
        gaps.append(top["score"] - acc["score"])
        if top["creation_date"] > acc["creation_date"]:
            later += 1
    counts = sorted(len(q["answers"]) for q in good)
    many = [q for q in good if len(q["answers"]) >= 5]
    mk = sum(1 for q in many if rule.not_top(q))
    mv = rule.verdict(mk, len(many))

    lines += [
        "",
        "COHORT %s  (%s)" % (cohort, "2013" if cohort == "old" else "2023"),
        "  rows fetched          %d" % len(rows),
        "  eligible              %d" % n,
        "  dropped               %s" % (dropped or "{}"),
        "  accepted not top      %d" % k,
        "  f                     %.4f  (%.4f .. %.4f)" % (v["f"], v["lo"], v["hi"]),
        "  VERDICT               %s   (bar %.2f)" % (v["verdict"].upper(), rule.BAR),
        "  control f (random)    %.4f   margin %+.4f  -> %s" % (
            cf, cf - v["f"], "ok" if control_ok else "VOID"),
        "  --- registered, not scored ---",
        "  exact ties at the top %d  (%.1f %%)" % (ties, 100.0 * ties / n if n else 0),
        "  gap median / p90      %s / %s" % (
            statistics.median(gaps) if gaps else "-",
            sorted(gaps)[int(0.9 * len(gaps))] if gaps else "-"),
        "  top posted later      %d of %d  (%.1f %%)" % (
            later, len(gaps), 100.0 * later / len(gaps) if gaps else 0),
        "  answers median / p90  %s / %s" % (
            statistics.median(counts) if counts else "-",
            counts[int(0.9 * len(counts))] if counts else "-"),
        "  f among >=5 answers   %.4f  (%.4f .. %.4f)  n=%d  %s" % (
            mv["f"], mv["lo"], mv["hi"], len(many), mv["verdict"]),
    ]
    return {"cohort": cohort, "n": n, "k": k, **v, "control": cf,
            "control_ok": control_ok, "later": later, "gaps": len(gaps)}


def write_rows(path=None):
    """One line per eligible question, so the verdict can be recomputed."""
    path = path or os.path.join(os.path.dirname(OUT), "questions.csv")
    with open(path, "w") as fh:
        fh.write("cohort,question_id,created,answers,accepted_score,top_score,"
                 "gap,not_top,top_posted_later\n")
        for cohort in ("old", "new"):
            for q in load(cohort):
                if rule.eligible(q):
                    continue
                acc = [a for a in q["answers"] if a.get("is_accepted")][0]
                top = max(q["answers"], key=lambda a: a["score"])
                fh.write("%s,%d,%d,%d,%d,%d,%d,%d,%d\n" % (
                    cohort, q["question_id"], q["creation_date"],
                    len(q["answers"]), acc["score"], top["score"],
                    top["score"] - acc["score"], int(rule.not_top(q)),
                    int(top["creation_date"] > acc["creation_date"])))


def main():
    lines = ["Stack Overflow: is the accepted answer the top-voted one?",
             "Rule sealed 2026-09-16 as rule.so-accepted-answer, before the data.",
             "Claim under test: the accepted answer is USUALLY not the best one.",
             "Verdict bar: survived lo>0.50, killed hi<0.50, else inconclusive."]
    out = [report(c, lines) for c in ("old", "new")]
    lines += ["", "SUMMARY"]
    for r in out:
        lines.append("  %-4s n=%-5d f=%.3f  %s%s" % (
            r["cohort"], r["n"], r["f"], r["verdict"].upper(),
            "" if r["control_ok"] else "  [CONTROL FAILED - VOID]"))
    write_rows()
    text = "\n".join(lines) + "\n"
    print(text)
    with open(OUT, "w") as fh:
        fh.write(text)


if __name__ == "__main__":
    main()
