#!/usr/bin/env python3
"""Check all-review inputs against the evidence, then reuse the original rubric."""

from collections import Counter
from copy import deepcopy
import json
from pathlib import Path
import re
import runpy


ROOT = Path(__file__).resolve().parent
SEVERITIES = {"blocker": "blocker", "high": "high", "medium": "P2", "low": "P3", "nit": "P3"}


def read(name):
    return json.loads((ROOT / name).read_text())


def validate_and_calculate():
    original = read("assessment.json")
    extended = read("full-review-assessment.json")
    authors = read("author-side/findings.json")["findings"]
    assignments = read("author-category-assignments.json")["assignments"]
    independent = [row for row in read("counts-public-findings.json")["findings"]
                   if row["includedByRetainedCorrectionRule"]]
    by_id = {row["record_id"]: row for row in authors}
    assigned = {row["record_id"]: row for row in assignments}
    assert len(by_id) == len(authors) == len(assigned) == len(assignments) == 585
    assert by_id.keys() == assigned.keys(), "Every author finding needs one explicit category assignment"
    assert len(independent) == original["cohort"]["retained_correction_threads"] == 233
    assert extended["scored_finding_records"] == len(authors) + len(independent)
    for key in ("rubric", "scenarios", "aggregate_groups"):
        assert original[key] == extended[key], f"The original {key} must not change"
    original_categories = {row["id"]: row for row in original["categories"]}
    extended_categories = {row["id"]: row for row in extended["categories"]}
    assert original_categories.keys() == extended_categories.keys()
    for identity, row in original_categories.items():
        for field in ("weight", "projected_anchor", "operation"):
            assert row[field] == extended_categories[identity][field], f"Changed {identity} {field}"
    counts = Counter((row["rubricCategory"], SEVERITIES[row["severity"]]) for row in independent)
    for identity, row in by_id.items():
        assignment = assigned[identity]
        category = assignment["category"]
        assert category in original_categories
        assert row["recorded_resolution"] == "fixed"
        assert assignment["rationale"].strip(), f"Missing assignment rationale: {identity}"
        if row["stage"] == "build":
            rules = original["category_assignment"]["ordered_regex_rules"]
            expected = next((rule["category"] for rule in rules if re.search(rule["regex"], row["source_path"])),
                            original["category_assignment"]["fallback_category"])
            assert category == expected, f"Build path mapping differs for {identity}"
            assert assignment["method"] == "existing_path_rule"
        else:
            assert row["stage"] == "plan" and row["source_path"] is None
            assert assignment["method"] == "analyst_judgment"
        counts[category, SEVERITIES[row["recorded_severity"]]] += 1
    for identity, row in extended_categories.items():
        expected = {severity: counts[identity, severity] for severity in ("blocker", "high", "P2", "P3")}
        assert row["fixed_finding_counts"] == expected, f"Combined counts differ: {identity}"
        assert row["fixed_finding_total"] == sum(expected.values())
    assert extended["finding_severity_totals"] == {
        severity: sum(count for (category, level), count in counts.items() if level == severity)
        for severity in ("blocker", "high", "P2", "P3")
    }
    engine = runpy.run_path(str(ROOT / "recompute.py"))
    engine["validate_finding_rows"](original, ROOT / "counts-public-findings.json")
    assert engine["calculate"](original) == original["calculation_results"]
    calculated = engine["calculate"](extended)
    assert calculated == extended["calculation_results"], "Full-review stored scores differ"
    # Excluding the pathless plan findings gives a separately labeled sensitivity,
    # not an alternative all-review result.
    build_only = deepcopy(extended)
    build_only["scored_finding_records"] = len(independent) + sum(row["stage"] == "build" for row in authors)
    for row in build_only["categories"]:
        row["fixed_finding_counts"] = dict(original_categories[row["id"]]["fixed_finding_counts"])
    build_categories = {row["id"]: row for row in build_only["categories"]}
    for identity, row in by_id.items():
        if row["stage"] == "build":
            build_categories[assigned[identity]["category"]]["fixed_finding_counts"][SEVERITIES[row["recorded_severity"]]] += 1
    build_only["finding_severity_totals"] = {
        severity: sum(row["fixed_finding_counts"][severity] for row in build_only["categories"])
        for severity in ("blocker", "high", "P2", "P3")
    }
    partial = engine["calculate"](build_only)
    assert partial == extended["build_only_calculation_results"], "Build-only sensitivity differs"
    return calculated


if __name__ == "__main__":
    print(json.dumps(validate_and_calculate(), indent=2, ensure_ascii=False))
