"""Every number that appears in the write-up, computed from repos.csv.

    python research/paper-code-links/analyse.py

Writes analysis.txt. Nothing here may change the verdict: the kill rule is in
rule.py, sealed before the data, and measure.py applied it. This only describes.
"""

import csv
import math
import os

HERE = os.path.dirname(os.path.abspath(__file__))
SUSPECT = "https://github.com/zhangchuangxin71-cyber/dynamic_"


def wilson(k, n, z=1.96):
    """Wilson interval — it does not run off the end of [0,1] near a rate of 1."""
    if n == 0:
        return (float("nan"), float("nan"))
    p = k / n
    d = 1 + z * z / n
    centre = (p + z * z / (2 * n)) / d
    half = z * math.sqrt(p * (1 - p) / n + z * z / (4 * n * n)) / d
    return centre - half, centre + half


def main():
    rows = list(csv.DictReader(open(os.path.join(HERE, "repos.csv"))))
    out = ["research/paper-code-links — every number in the write-up", ""]
    out.append(f"rows: {len(rows)}  "
               f"REFUSED: {sum(1 for r in rows if r['class'] == 'REFUSED')}  "
               f"OTHER: {sum(1 for r in rows if r['class'] == 'OTHER')}")
    out.append("(no repository refused this server and none answered anything but")
    out.append(" 200 or 404, so the refusal carve-out in the rule never had to fire)")
    out.append("")

    stats = {}
    for cohort in ("old", "recent"):
        r = [x for x in rows if x["cohort"] == cohort]
        n = sum(1 for x in r if x["class"] != "REFUSED")
        k = sum(1 for x in r if x["class"] == "ALIVE")
        lo, hi = wilson(k, n)
        stats[cohort] = (n, k, k / n)
        out.append(f"{cohort:7} n={n:3} alive={k:3} gone={n-k:2} "
                   f"rate={k/n:.4f}  95% Wilson [{lo:.4f}, {hi:.4f}]")

    (no, ko, po), (nr, kr, pr) = stats["old"], stats["recent"]
    d = po - pr
    se = math.sqrt(po * (1 - po) / no + pr * (1 - pr) / nr)
    out.append("")
    out.append(f"old - recent = {d:+.4f}  95% [{d - 1.96*se:+.4f}, {d + 1.96*se:+.4f}]")
    out.append("Seven to eight years of ageing moves availability by less than one")
    out.append("point and the interval covers zero comfortably. The dead ones are")
    out.append("not rotted: they are the same share dead in a cohort months old.")
    out.append("")

    # The finding that is not in the kill rule: who is doing the remembering.
    for cohort in ("old", "recent"):
        alive = [x for x in rows if x["cohort"] == cohort and x["class"] == "ALIVE"]
        moved = [x for x in alive
                 if x["final"].rstrip("/").lower() != x["url"].rstrip("/").lower()]
        out.append(f"{cohort:7} {len(moved):2} of {len(alive)} live repositories "
                   f"answered only after a redirect ({len(moved)/len(alive):.1%})")
    alive_old = [x for x in rows if x["cohort"] == "old" and x["class"] == "ALIVE"]
    moved_old = [x for x in alive_old
                 if x["final"].rstrip("/").lower() != x["url"].rstrip("/").lower()]
    without = (len(alive_old) - len(moved_old)) / stats["old"][0]
    out.append("")
    out.append(f"If GitHub stopped honouring renames, the 2018 rate would be "
               f"{without:.4f} rather than {po:.4f}.")
    out.append("The string printed in the paper is wrong in those cases. The")
    out.append("platform is doing the remembering, not the citation.")
    out.append("")

    # Sensitivity to the one URL whose shape suggests a truncation.
    r = [x for x in rows if x["cohort"] == "recent" and x["url"] != SUSPECT]
    n = sum(1 for x in r if x["class"] != "REFUSED")
    k = sum(1 for x in r if x["class"] == "ALIVE")
    out.append(f"One recent URL ends in '_', which is the shape of a truncation "
               f"in the abstract or in my normaliser.")
    out.append(f"Dropping it: recent n={n} k={k} rate={k/n:.4f} — the gate still "
               f"passes and no verdict moves.")

    text = "\n".join(out)
    print(text)
    with open(os.path.join(HERE, "analysis.txt"), "w") as f:
        f.write(text + "\n")


if __name__ == "__main__":
    main()
