#!/usr/bin/env python3
"""
PeerCortex Final Quality Audit v4
Tests 103 ASNs against v0.6.7 (running, stable)
Uses cached responses where available, skips ASNs that don't respond in 15s
Cross-validates: prefix counts, RPKI math, IX dedup, facilities, RIR, country
"""
import json, urllib.request, time, sys
from datetime import datetime, timezone

def fetch(url, timeout=15):
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "PeerCortex-Audit/4.0"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return json.loads(r.read())
    except Exception as e:
        return None

ASNS = [
    1, 174, 714, 1299, 2497, 2516, 2906, 3257, 3303, 3320, 3356,
    4134, 4766, 5089, 5483, 5511, 6057, 6128, 6147, 6453, 6695,
    6762, 6830, 6939, 7018, 7473, 7545, 7922, 8075, 8151, 8220,
    8422, 8708, 9121, 9304, 9318, 9498, 10429, 10474, 10796, 12479,
    12714, 12956, 13237, 13335, 14080, 15133, 15169, 15600, 15704,
    16276, 16509, 17676, 18403, 19281, 20115, 20562, 20932, 20940,
    21928, 22773, 22822, 23106, 24115, 24429, 24940, 24961, 25091,
    29049, 32934, 33891, 34465, 34549, 35369, 37100, 38001, 39122,
    41327, 42, 42739, 43350, 43996, 44574, 45899, 47692, 48159,
    50629, 51088, 54113, 56630, 57695, 58057, 59947, 60068, 60501,
    62240, 64271, 132335, 136907, 137409, 139070, 197540, 199121,
]

results = {"ok": 0, "warn": 0, "crit": 0, "skip": 0, "total": len(ASNS)}
all_issues = []
responded = []

print(f"PeerCortex Final Audit v4 — {datetime.now(timezone.utc).isoformat()}")
print(f"Target: http://localhost:3101 | ASNs: {len(ASNS)}")
print("=" * 75)

for i, asn in enumerate(ASNS):
    sys.stdout.write(f"\r  [{i+1:3d}/{len(ASNS)}] AS{asn}...        ")
    sys.stdout.flush()

    d = fetch(f"http://localhost:3101/api/lookup?asn={asn}", timeout=15)
    if not d or "network" not in d or d.get("error"):
        results["skip"] += 1
        continue

    responded.append(asn)
    n   = d.get("network", {})
    p   = d.get("prefixes", {})
    r   = d.get("rpki", {})
    ix  = d.get("ix_presence", {})
    fac = d.get("facilities", {})
    meta= d.get("meta", {})
    reg = d.get("registration", {})

    issues = []

    # ── Math checks ─────────────────────────────────────────
    if p.get("ipv4", 0) + p.get("ipv6", 0) != p.get("total", -1):
        issues.append(("CRITICAL", "prefix_math",
            f"v4({p.get('ipv4')})+v6({p.get('ipv6')})={p.get('ipv4',0)+p.get('ipv6',0)} ≠ total({p.get('total')})"))

    rpki_sum = r.get("valid",0) + r.get("invalid",0) + r.get("not_found",0)
    if rpki_sum != r.get("checked", 0):
        issues.append(("CRITICAL", "rpki_sum",
            f"{rpki_sum} ≠ checked({r.get('checked')})"))

    if r.get("checked", 0) > 0:
        exp = round(r.get("valid",0) / r.get("checked") * 100)
        if abs(exp - r.get("coverage_percent", 0)) > 1:
            issues.append(("CRITICAL", "rpki_pct",
                f"expected {exp}% got {r.get('coverage_percent')}%"))

    conns = ix.get("connections", [])
    dedup = len(set(c.get("ix_id") for c in conns if c.get("ix_id")))
    if dedup != ix.get("unique_ixps", 0):
        issues.append(("CRITICAL", "ix_dedup",
            f"dedup={dedup} ≠ unique_ixps={ix.get('unique_ixps')}"))

    if len(conns) != ix.get("total_connections", 0):
        issues.append(("WARNING", "ix_total",
            f"len={len(conns)} ≠ total_connections={ix.get('total_connections')}"))

    if len(fac.get("list",[])) != fac.get("total", 0):
        issues.append(("WARNING", "fac_total",
            f"len={len(fac.get('list',[]))} ≠ total={fac.get('total')}"))

    # ── Mandatory fields ─────────────────────────────────────
    if not n.get("name") or n.get("name") == "Unknown":
        issues.append(("CRITICAL", "name_empty", f"name={n.get('name')!r}"))

    if not n.get("asn"):
        issues.append(("CRITICAL", "asn_empty", "ASN field missing"))

    if not n.get("rir") and not reg.get("rir"):
        issues.append(("WARNING", "rir_empty", f"rir empty, country={n.get('country')!r}"))

    if not n.get("country"):
        issues.append(("WARNING", "country_empty", "country field empty"))

    if meta.get("version") != "0.6.7":
        issues.append(("WARNING", "version", f"got {meta.get('version')}"))

    # ── Categorize ───────────────────────────────────────────
    crits = [x for x in issues if x[0] == "CRITICAL"]
    warns = [x for x in issues if x[0] == "WARNING"]

    if crits:
        results["crit"] += 1
    elif warns:
        results["warn"] += 1
    else:
        results["ok"] += 1

    if issues:
        all_issues.append((asn, n.get("name","?"), issues,
            p.get("total"), r.get("coverage_percent"),
            ix.get("total_connections"), fac.get("total"),
            n.get("rir"), n.get("country")))

print(f"\r  Done. {len(responded)} responded, {results['skip']} skipped.        ")

print()
print("=" * 75)
print(f"FINAL AUDIT SUMMARY — {len(responded)} ASNs responded ({results['skip']} timed out)")
print("=" * 75)
print(f"  ✓ PERFECT (zero issues):   {results['ok']:3d}")
print(f"  ⚠ WARNING only:            {results['warn']:3d}")
print(f"  ✗ CRITICAL:                {results['crit']:3d}")
print(f"  ⏭ Skipped (no response):   {results['skip']:3d}")

if results["crit"] == 0:
    print("\n  ✅ NO CRITICAL DATA ERRORS IN ANY RESPONDED ASN")

# Per-category counts
warn_counts = {}
crit_counts = {}
for _, _, issues, *_ in all_issues:
    for sev, field, _ in issues:
        if sev == "CRITICAL":
            crit_counts[field] = crit_counts.get(field, 0) + 1
        else:
            warn_counts[field] = warn_counts.get(field, 0) + 1

if crit_counts:
    print("\n  CRITICAL by field:")
    for f, c in sorted(crit_counts.items(), key=lambda x: -x[1]):
        print(f"    {f:35s}: {c} ASNs")

if warn_counts:
    print("\n  WARNING by field:")
    for f, c in sorted(warn_counts.items(), key=lambda x: -x[1]):
        print(f"    {f:35s}: {c} ASNs")

# Detail for any issues
if all_issues:
    print("\n  DETAILS:")
    for asn, name, issues, pfx, rpki_pct, ix_cnt, fac_cnt, rir, cc in all_issues:
        for sev, field, msg in issues:
            print(f"    [{sev[:4]}] AS{asn:>7} | {field:30s} | {msg[:60]}")

print()
print("  SAMPLE DATA (responded ASNs):")
print(f"  {'ASN':>8} {'Name':24s} {'RIR':>6} {'CC':>3} {'Pfx':>5} {'RPKI%':>6} {'IX':>4} {'Fac':>4}")
print(f"  {'-'*70}")
for asn in responded[:25]:
    d = fetch(f"http://localhost:3101/api/lookup?asn={asn}", timeout=5)
    if not d: continue
    n=d.get("network",{}); p=d.get("prefixes",{}); r=d.get("rpki",{}); ix=d.get("ix_presence",{}); fac=d.get("facilities",{})
    print(f"  AS{asn:>6} {n.get('name','?')[:22]:24s} {n.get('rir','?'):>6} {n.get('country','?'):>3} {p.get('total',0):>5} {r.get('coverage_percent',0):>5}% {ix.get('total_connections',0):>4} {fac.get('total',0):>4}")

print()
print(f"Audit complete: {datetime.now(timezone.utc).isoformat()}")
