#!/usr/bin/env python3
"""Independently recount the published factual extract using Python's standard library."""

import argparse
from collections import Counter, defaultdict
import json
import math
from pathlib import Path
from statistics import median


def recount(records):
    # Publication intentionally requires the actual sample to have no exclusions.
    # A different sample needs an explicit attrition-aware publication workflow.
    assert len(records) == 500
    assert len({r["id"] for r in records}) == len(records)
    assert all(r["outcomeType"] == "BINARY" and r["token"] == "MANA"
               and r["isResolved"] is True for r in records)
    assert all(r["resolution"] in ("YES", "NO", "CANCEL", "MKT") for r in records)
    assert all(isinstance(r["creatorId"], str) and r["creatorId"] for r in records)
    assert all(type(r["volume"]) in (int, float) and math.isfinite(r["volume"])
               and r["volume"] >= 0 for r in records)
    assert all(a["resolutionTime"] >= b["resolutionTime"]
               for a, b in zip(records, records[1:]))
    counts = Counter(r["resolution"] for r in records)
    groups = defaultdict(list)
    for record in records:
        groups[record["creatorId"]].append(record)
    ranked = sorted(groups, key=lambda c: (-len(groups[c]), c))
    outside = lambda rows: sum(r["resolution"] not in ("YES", "NO") for r in rows)
    largest = ranked[0]
    largest_outside = min(groups, key=lambda c: (-outside(groups[c]), c))
    remaining = [r for r in records if r["creatorId"] != largest]
    remaining_outside = [r for r in records if r["creatorId"] != largest_outside]
    kept = [r for r in records if r["resolution"] in ("YES", "NO")]
    volume = math.fsum(r["volume"] for r in records)
    excluded_volume = math.fsum(r["volume"] for r in records
                               if r["resolution"] not in ("YES", "NO"))
    mkt = [r["resolutionProbability"] for r in records if r["resolution"] == "MKT"]
    assert all(type(p) in (int, float) and math.isfinite(p) and 0 <= p <= 1 for p in mkt)
    assert all(type(r["createdTime"]) is int and 0 < r["createdTime"] <= r["resolutionTime"] for r in records)
    return {
        "records": len(records),
        "label_counts": {label: counts[label] for label in ("YES", "NO", "CANCEL", "MKT")},
        "outside_yes_no_records": outside(records),
        "outside_yes_no_percent": round(100 * outside(records) / len(records), 10),
        "retained_yes_no_records": len(kept),
        "creators": len(groups),
        "retained_yes_no_creators": len({r["creatorId"] for r in kept}),
        "largest_creator": {"code": largest, "records": len(groups[largest]),
                            "label_counts": dict(sorted(Counter(r["resolution"] for r in groups[largest]).items()))},
        "top_five_creator_records": sum(len(groups[c]) for c in ranked[:5]),
        "without_largest_creator": {"records": len(remaining), "outside_yes_no_records": outside(remaining),
                                    "outside_yes_no_percent": round(100 * outside(remaining) / len(remaining), 10)},
        "without_largest_outside_creator": {"code": largest_outside, "records": len(remaining_outside),
                                            "outside_yes_no_records": outside(remaining_outside),
                                            "outside_yes_no_percent": round(100 * outside(remaining_outside) / len(remaining_outside), 10)},
        "equal_creator_weight_outside_yes_no_percent": round(100 * math.fsum(outside(g) / len(g) for g in groups.values()) / len(groups), 10),
        "reported_lifetime_mana_volume": volume,
        "outside_yes_no_mana_volume": excluded_volume,
        "outside_yes_no_mana_volume_percent": round(100 * excluded_volume / volume, 10),
        "mkt_probability_min": min(mkt) if mkt else None,
        "mkt_probability_max": max(mkt) if mkt else None,
        "mkt_strictly_fractional_records": sum(0 < p < 1 for p in mkt),
        "median_age_days_by_label": {
            label: median((r["resolutionTime"] - r["createdTime"]) / 86_400_000
                          for r in records if r["resolution"] == label)
            for label in ("YES", "NO", "CANCEL", "MKT")
        },
        "resolution_time_min_ms": min(r["resolutionTime"] for r in records),
        "resolution_time_max_ms": max(r["resolutionTime"] for r in records),
    }


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("directory", type=Path, nargs="?", default=Path(__file__).resolve().parent)
    directory = parser.parse_args().directory
    evidence = json.loads((directory / "evidence.json").read_text())
    expected = json.loads((directory / "summary.json").read_text())
    actual = recount(evidence["records"])
    if actual != expected:
        raise SystemExit("FAIL: published summary does not match the evidence")
    print("PASS: all published summary statistics reproduce from the 500 evidence records")
    print(json.dumps(actual, indent=2))
