#!/usr/bin/env python3
"""Reproduce the October 16, 2026 dataset composition analysis.
Python 3.9+; standard library only. No network requests.
Usage: python3 reproduce.py SNAPSHOT.json OUTPUT_DIRECTORY
CC BY 4.0: credit the source publication and identify changes.
"""
import csv
import hashlib
import json
import sys
from collections import Counter
from html import escape
from pathlib import Path

SNAPSHOTS = [{'sha256': '9e6881b9d74f44c7a1c9ee0b976200e4555c9e65e9999159212e80b1003acdb8', 'row': 'ai_involvement', 'column': 'failure_type'}, {'sha256': '1cd10bf6caf2e253bdaccd41818dfdec2f8ab27b15cb76a7aac3a88ea31d8d61', 'row': 'category', 'column': 'evidence_status'}]

def main():
    if len(sys.argv) != 3:
        raise SystemExit(__doc__)
    source, out = Path(sys.argv[1]), Path(sys.argv[2])
    digest = hashlib.sha256(source.read_bytes()).hexdigest()
    config = next((x for x in SNAPSHOTS if x["sha256"] == digest), None)
    if config is None:
        raise SystemExit("Input hash does not match either published analysis snapshot.")
    data = json.loads(source.read_text(encoding="utf-8"))
    records = data["records"]
    if len({r["id"] for r in records}) != len(records):
        raise SystemExit("Duplicate record IDs; refusing to count.")
    row, column = config["row"], config["column"]
    rows = sorted({r[row] for r in records})
    columns = sorted({r[column] for r in records})
    counts = Counter((r[row], r[column]) for r in records)
    total = len(records)
    cells = [{row:a, column:b, "count":counts[a,b], "denominator":total,
              "percent_of_all_records":round(100*counts[a,b]/total, 2)}
             for a in rows for b in columns]
    fields = [f for f in (row,column,"record_type","date_precision") if f in records[0]]
    summary = {"analysis_date":"2026-10-16", "dataset":data["title"],
               "version":data["version"], "released":data["released"],
               "source_url":data["url"], "snapshot_sha256":digest,
               "denominator":total, "unit":"one published record",
               "limitations":"Curated collection; not prevalence, model accuracy, severity or a time trend. Studies count once, not by trial count.",
               "row_field":row, "column_field":column,
               "marginals":{f:dict(sorted(Counter(r[f] for r in records).items())) for f in fields},
               "cells":cells}
    out.mkdir(parents=True, exist_ok=True)
    (out/"summary.json").write_text(json.dumps(summary,ensure_ascii=False,indent=2)+"\n",encoding="utf-8")
    with (out/"counts.csv").open("w",newline="",encoding="utf-8") as f:
        writer=csv.DictWriter(f,fieldnames=list(cells[0]));writer.writeheader();writer.writerows(cells)
    colors=["#344f3c","#ac472f","#746291","#47778c"]
    height=210+len(rows)*80+len(columns)*28
    svg=[f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 {height}" role="img">',
         f'<title>{escape(data["title"])} v{escape(data["version"])}: record composition</title>',
         '<desc>Counts by recorded labels. The adjacent HTML table supplies every value. This curated collection does not measure failure rates.</desc>',
         f'<rect width="400" height="{height}" fill="#faf5ed"/>',
         '<g font-family="sans-serif" fill="#242424">',
         f'<text x="16" y="30" font-size="20">{total} selected records · v{escape(data["version"])}</text>',
         '<text x="16" y="55" font-size="14">Composition, not frequency or risk</text>']
    max_row=max(sum(counts[a,b] for b in columns) for a in rows)
    for i,a in enumerate(rows):
        y=90+i*80;n=sum(counts[a,b] for b in columns)
        svg.append(f'<text x="16" y="{y}" font-size="17">{escape(a.capitalize())}: {n}</text>')
        x=16
        for j,b in enumerate(columns):
            w=320*counts[a,b]/max_row
            if w:
                svg.append(f'<rect x="{x:.2f}" y="{y+12}" width="{w:.2f}" height="28" fill="{colors[j]}"><title>{escape(b)}: {counts[a,b]}</title></rect>')
            x+=w
    start=90+len(rows)*80
    for j,b in enumerate(columns):
        y=start+j*28
        svg += [f'<rect x="16" y="{y-12}" width="14" height="14" fill="{colors[j]}"/>',f'<text x="40" y="{y}" font-size="15">{escape(b.capitalize())}</text>']
    credit=data["url"].removeprefix("https://www.")
    svg += [f'<text x="16" y="{height-54}" font-size="13">Source: {escape(credit)}</text>',f'<text x="16" y="{height-30}" font-size="13">Snapshot {escape(data["released"])} · CC BY 4.0</text>','</g></svg>']
    (out/"chart.svg").write_text("".join(svg)+"\n",encoding="utf-8")
    print(json.dumps(summary,ensure_ascii=False))

if __name__ == "__main__":
    main()
