#!/usr/bin/env python3
"""Summarize the adjudicated diversity-oriented manual audit without population generalization."""
from __future__ import annotations

import csv
import json
from collections import Counter
from pathlib import Path

import numpy as np

ROOT = Path(__file__).resolve().parents[2]
INPUT = ROOT / "research-output" / "university-manual-audit.csv"
OUTPUT = ROOT / "research-output" / "university-manual-results.json"
SEED = 20260810
REPS = 10000


def read() -> list[dict]:
    with INPUT.open(newline="", encoding="utf-8") as stream:
        return list(csv.DictReader(stream))


def mean_boot(values: list[float], seed: int) -> dict:
    a = np.asarray(values, dtype=float)
    if not len(a):
        return {"n": 0, "estimate": None, "ci95": [None, None]}
    rng = np.random.default_rng(seed)
    draws = rng.choice(a, size=(REPS, len(a)), replace=True).mean(axis=1)
    return {"n": len(a), "estimate": float(a.mean()), "ci95": [float(np.quantile(draws, .025)), float(np.quantile(draws, .975))], "method": f"audit_sample_paired_bootstrap_{REPS}_reps"}


def binary(value: str, positive: set[str]) -> float | None:
    return None if value in {"", "not_present", "unclear"} else float(value in positive)


def rate(rows: list[dict], field: str, condition=lambda x: True) -> dict:
    values = [float(r[field]) for r in rows if r[field] != "" and condition(r)]
    return mean_boot(values, SEED + sum(ord(x) for x in field))


def main() -> None:
    rows = read()
    assert len(rows) == 230
    eligible = [r for r in rows if r["eligible_news"] == "1"]
    paired = [r for r in eligible if r["admin_present"] == "1" and r["challenger_present"] == "1"]
    scrutiny = [float(r["admin_skepticism"]) - float(r["challenger_skepticism"]) for r in paired if r["admin_skepticism"] != "" and r["challenger_skepticism"] != ""]
    tone = [float(r["admin_tone"]) - float(r["challenger_tone"]) for r in paired if r["admin_tone"] != "" and r["challenger_tone"] != ""]
    official_test = [float(r["official_assertion_tested"]) - float(r["challenger_assertion_tested"]) for r in paired if r["official_assertion_tested"] != "" and r["challenger_assertion_tested"] != ""]
    headline, lede = [], []
    for r in paired:
        ah = binary(r["admin_headline_frame"], {"adverse"})
        ch = binary(r["challenger_headline_frame"], {"adverse"})
        al = binary(r["admin_lede_frame"], {"adverse"})
        cl = binary(r["challenger_lede_frame"], {"adverse"})
        if ah is not None and ch is not None:
            headline.append(ah - ch)
        if al is not None and cl is not None:
            lede.append(al - cl)
    first_actor = [float(r["first_actor"] == "administration") - float(r["first_actor"] == "challenger") for r in paired]
    first_quote = [float(r["first_quoted_actor"] == "administration") - float(r["first_quoted_actor"] == "challenger") for r in paired]

    # Illustrative cases are selected by fixed observable criteria, not handpicked tone alone.
    strong_reporting = [
        r["article_id"] for r in eligible
        if r["article_type"] == "investigative_enterprise"
        and r["documents_records_data"] == "1"
        and r["power_history_consequences_context"] == "1"
        and (r["admin_skepticism"] in {"2", "3"} or r["challenger_skepticism"] in {"2", "3"})
    ][:20]
    admin_deference_candidates = [
        r["article_id"] for r in paired
        if r["release_reliance"] in {"2", "3"}
        and r["admin_skepticism"] == "0"
        and r["challenger_skepticism"] not in {"", "0"}
    ][:20]
    contrary_scrutiny_cases = [
        r["article_id"] for r in paired
        if r["admin_skepticism"] in {"2", "3"}
        and r["challenger_skepticism"] in {"0", "1"}
    ][:20]
    payload = {
        "status": "diversity_oriented_manual_validation_sample_not_population_estimate",
        "sample_n": len(rows),
        "sample_share_of_census": len(rows) / 1527,
        "eligible_news_n": len(eligible),
        "paired_actor_n": len(paired),
        "article_type_counts": dict(Counter(r["article_type"] for r in eligible)),
        "paired_estimands": {
            "admin_minus_challenger_scrutiny": mean_boot(scrutiny, SEED + 1),
            "admin_minus_challenger_tone": mean_boot(tone, SEED + 2),
            "admin_minus_challenger_adverse_headline": mean_boot(headline, SEED + 3),
            "admin_minus_challenger_adverse_lede": mean_boot(lede, SEED + 4),
            "official_minus_challenger_assertion_tested": mean_boot(official_test, SEED + 5),
            "admin_minus_challenger_first_actor": mean_boot(first_actor, SEED + 6),
            "admin_minus_challenger_first_quote": mean_boot(first_quote, SEED + 7),
        },
        "article_level_rates": {
            "release_reliance_ge_2": mean_boot([float(int(r["release_reliance"]) >= 2) for r in eligible if r["release_reliance"] != ""], SEED + 8),
            "documents_records_data": rate(eligible, "documents_records_data"),
            "affected_people_consulted": rate(eligible, "affected_people_consulted"),
            "power_history_consequences_context": rate(eligible, "power_history_consequences_context"),
            "follow_up_orientation": rate(eligible, "follow_up_orientation"),
        },
        "evidence_case_ids": {
            "strong_investigative_or_enterprise": strong_reporting,
            "administrative_deference_screen": admin_deference_candidates,
            "greater_admin_scrutiny_counterexamples": contrary_scrutiny_cases,
        },
        "caution": "The sample deliberately maximizes screen/stratum diversity and is not self-weighting. Intervals describe resampling stability inside the audit set; they are not population confidence intervals for all 1,527 articles.",
    }
    OUTPUT.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
    print(json.dumps(payload, indent=2))


if __name__ == "__main__":
    main()
