#!/usr/bin/env python3
"""Classify camera-EVB parity records against the schematic netlist and board audit."""
from collections import Counter
import hashlib
import json
from pathlib import Path
import re
import xml.etree.ElementTree as ET

HERE = Path(__file__).resolve().parent
PARITY_PATH = HERE / "reports/camera_evb_a0_parity.json"
NETLIST_PATH = HERE / "camera_evb_a0.xml"
PINMAP_PATH = HERE / "camera_evb_a0_netcheck.json"
PARITY = json.loads(PARITY_PATH.read_text(encoding="utf-8"))
PINMAP = json.loads(PINMAP_PATH.read_text(encoding="utf-8"))

counts = Counter(row["type"] for row in PARITY["schematic_parity"])
flat_net_names = 0
no_connect_findings = set()
unclassified = []
for row in PARITY["schematic_parity"]:
    description = row["description"]
    match = re.fullmatch(r"Pad net \((.*?)\) doesn't match net given by schematic \((.*?)\)", description)
    if match:
        pcb_net, schematic_net = match.groups()
        if pcb_net == schematic_net.lstrip("/"):
            flat_net_names += 1
        else:
            unclassified.append(description)
        continue
    match = re.fullmatch(r"Pad missing net given by schematic \(unconnected-\(([^-]+)-.*-Pad(\d+)\)\)", description)
    if match:
        ref, pin = match.groups()
        no_connect_findings.add(f"{ref}.{pin}")
    else:
        unclassified.append(description)

xml_root = ET.parse(NETLIST_PATH).getroot()
xml_no_connects = set()
for net in xml_root.findall("./nets/net"):
    if net.get("name", "").startswith("unconnected-("):
        for node in net.findall("node"):
            xml_no_connects.add(f"{node.get('ref')}.{node.get('pin')}")

erc = (HERE / "reports/camera_evb_a0_erc.rpt").read_text(encoding="utf-8")
assert counts == {"net_conflict": 141}
assert flat_net_names == PINMAP["connected_schematic_pins"] == 59
assert no_connect_findings == xml_no_connects
assert len(xml_no_connects) == PINMAP["explicit_no_connect_pins"] == 82
assert not unclassified
assert not PARITY["violations"] and not PARITY["unconnected_items"]
assert "** ERC messages: 0  Errors 0  Warnings 0" in erc
assert PINMAP["status"] == "PASS" and PINMAP["net_mismatches"] == []
assert PINMAP["missing_on_board"] == [] and PINMAP["unexpected_board_pads_absent_from_schematic"] == []
assert all(pair["skew_mm"] <= PINMAP["limits"]["maximum_pair_skew_mm"] for pair in PINMAP["csi_route_lengths"].values())

inputs = [PARITY_PATH, NETLIST_PATH, PINMAP_PATH, HERE / "camera_evb_a0.kicad_sch", HERE / "camera_evb_a0.kicad_pcb"]
result = {
    "date": "2026-10-09",
    "status": "WARNINGS_CLASSIFIED_NOT_SUPPRESSED",
    "source_sha256": {path.name: hashlib.sha256(path.read_bytes()).hexdigest() for path in inputs},
    "parity_warning_records": dict(counts),
    "classification": {
        "flat_pcb_name_vs_root_sheet_slash_prefix": flat_net_names,
        "explicit_schematic_no_connects_with_unnetted_board_pads": len(no_connect_findings),
        "unclassified": 0,
    },
    "independent_checks": {
        "erc_errors": 0,
        "erc_warnings": 0,
        "drc_violations": len(PARITY["violations"]),
        "drc_unconnected_items": len(PARITY["unconnected_items"]),
        "connected_pad_mismatches": len(PINMAP["net_mismatches"]),
        "connected_pads": PINMAP["connected_schematic_pins"],
        "explicit_no_connect_pins": PINMAP["explicit_no_connect_pins"],
        "maximum_csi_pair_skew_mm": max(pair["skew_mm"] for pair in PINMAP["csi_route_lengths"].values()),
    },
    "qualification": {
        "physical_connector_orientation_verified": False,
        "impedance_verified": False,
        "signal_integrity_verified": False,
        "hardware_safety_verified": False,
        "product_fit_verified": False,
        "manufacturing_release": False,
    },
    "notes": [
        "59 connected-pad warnings differ only by the root-sheet slash prefix; each PCB net name matches the schematic suffix.",
        "82 remaining warnings correspond to explicit unconnected schematic pins and unnetted PCB pads; the independent pin-map check passes.",
        "The original warning records remain intact; this classification does not suppress them or establish electrical performance.",
    ],
}
(HERE / "camera_evb_parity_classification.json").write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps(result, ensure_ascii=False, indent=2))
