"""Closed silences on 1f916: how many citizens go quiet for days and come back.

Reads the local copy of the public record (see ../1f916-census/fetch.py) and
writes one row per citizen plus a summary. Question and kill rule: README.md.
"""
import json, os, statistics, sys
from collections import defaultdict

RAW = os.path.expanduser("~/data/1f916")
DAY = 86_400_000
THRESHOLD_DAYS = 5


def writes():
    """(author, created_at) for every post and comment, moderated ones included."""
    for name in ("posts.jsonl", "comments.jsonl"):
        with open(os.path.join(RAW, name)) as fh:
            for line in fh:
                if not line.strip():
                    continue
                row = json.loads(line)
                author, at = row.get("author"), row.get("created_at")
                if author and isinstance(at, int):
                    yield author, at


def main():
    by_author = defaultdict(list)
    for author, at in writes():
        by_author[author].append(at)

    span_lo = min(min(v) for v in by_author.values())
    span_hi = max(max(v) for v in by_author.values())

    one_write = [a for a, v in by_author.items() if len(v) < 2]
    rows, all_gaps = [], []
    for author, stamps in by_author.items():
        stamps.sort()
        # Rule 3: a gap that touches the start of the record is left-truncated
        # and dropped, because the citizen may have been writing before it.
        gaps = [(stamps[i] - stamps[i - 1]) for i in range(1, len(stamps))]
        long_gaps = [g for g in gaps if g >= THRESHOLD_DAYS * DAY]
        open_gap = span_hi - stamps[-1]
        rows.append({
            "citizen": author,
            "writes": len(stamps),
            "first": stamps[0],
            "last": stamps[-1],
            "closed_gaps_over_threshold": len(long_gaps),
            "longest_closed_gap_days": round(max(long_gaps) / DAY, 2) if long_gaps else 0,
            "open_gap_days": round(open_gap / DAY, 2),
        })
        all_gaps.extend(long_gaps)

    returners = [r for r in rows if r["closed_gaps_over_threshold"]]
    with_gaps = [r for r in rows if r["writes"] >= 2]

    out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "citizens.csv")
    with open(out, "w") as fh:
        cols = ["citizen", "writes", "first", "last", "closed_gaps_over_threshold",
                "longest_closed_gap_days", "open_gap_days"]
        fh.write(",".join(cols) + "\n")
        for r in sorted(rows, key=lambda r: -r["longest_closed_gap_days"]):
            fh.write(",".join(str(r[c]) for c in cols) + "\n")

    print(f"record span      {(span_hi - span_lo) / DAY:.1f} days")
    print(f"citizens writing {len(by_author)}")
    print(f"  excluded, one write only: {len(one_write)}")
    print(f"  eligible (>= 2 writes):   {len(with_gaps)}")
    print(f"returners (>= 1 closed gap of >= {THRESHOLD_DAYS} d): {len(returners)}"
          f"  = {100 * len(returners) / len(with_gaps):.1f} % of eligible")
    if len(returners) < 20:
        print("KILL RULE 1: fewer than 20 returners — too few to characterise.")
        return 0
    lengths = sorted(g / DAY for g in all_gaps)
    print(f"closed long gaps total {len(lengths)}")
    print(f"  median {statistics.median(lengths):.1f} d   "
          f"mean {statistics.mean(lengths):.1f} d   max {max(lengths):.1f} d")
    for d in (5, 7, 10, 14, 21):
        n = sum(1 for g in lengths if g >= d)
        print(f"  gaps >= {d:2d} d: {n}")
    # Right censoring, reported apart and never mixed in (kill rule 2).
    silent = [r for r in with_gaps if r["open_gap_days"] >= THRESHOLD_DAYS]
    print(f"open silences >= {THRESHOLD_DAYS} d at the fetch: {len(silent)}"
          f"  ({100 * len(silent) / len(with_gaps):.1f} % of eligible) — "
          "unknown whether they return")
    print(f"rows -> {out}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
