#!/usr/bin/env python3
"""
TDD tests for the claims pipeline (gen-claims.py + drift-check.py).

These assert the GROUND-TRUTH numbers an auditor would check. They are
written against the real product artifacts (no mocks) so a regression in
the product OR in the counting logic fails the test.

Run ONLY these (do not run the shared suite):
    python -m pytest scripts/test_claims.py -v
"""

import importlib.util
import json
import subprocess
import sys
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
REPO_ROOT = SCRIPT_DIR.parent


def _load(name, filename):
    spec = importlib.util.spec_from_file_location(name, SCRIPT_DIR / filename)
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod


def test_cve_count_is_real():
    gc = _load("gen_claims", "gen-claims.py")
    assert gc.get_cve_count() == 324593


def test_scanner_count_is_18():
    gc = _load("gen_claims", "gen-claims.py")
    count, ids = gc.get_scanner_count()
    assert count == 18
    assert len(set(ids)) == 18  # no duplicate ids


def test_framework_count_is_30():
    gc = _load("gen_claims", "gen-claims.py")
    table = gc.get_framework_table()
    assert len(table) == 30


def test_ai_backend_count_is_18():
    gc = _load("gen_claims", "gen-claims.py")
    count, ids = gc.get_ai_backend_count()
    assert count == 18


def test_known_framework_control_anchors():
    """Validate the counter against authoritative public control counts."""
    gc = _load("gen_claims", "gen-claims.py")
    table = {row["id"]: row["controls"] for row in gc.get_framework_table()}
    # ISO 27001:2022 Annex A = 93 controls (authoritative).
    assert table["iso_27001_2022"] == 93
    # CIS Controls v8 = 153 safeguards (authoritative).
    assert table["cis-controls-v8"] == 153
    # NIST CSF 2.0 = 106 subcategories (authoritative).
    assert table["nist_csf_2.0"] == 106
    # CMMC 2.0 Levels 1-3 = 110 practices (authoritative).
    assert table["cmmc"] == 110


def test_all_frameworks_have_nonzero_controls():
    gc = _load("gen_claims", "gen-claims.py")
    table = gc.get_framework_table()
    zero = [r["id"] for r in table if r["controls"] == 0]
    assert zero == [], f"frameworks with zero counted controls: {zero}"


def test_build_claims_marks_competitor_pricing_unprovable():
    gc = _load("gen_claims", "gen-claims.py")
    claims = gc.build_claims()
    assert claims["competitor_pricing"]["provable"] is False
    for v in claims["competitor_pricing"]["canonical_proposed"]["vendors"]:
        assert v["provable"] is False


def test_cli_json_runs():
    result = subprocess.run(
        [sys.executable, str(SCRIPT_DIR / "gen-claims.py"), "--json"],
        capture_output=True, text=True, cwd=str(REPO_ROOT),
    )
    assert result.returncode == 0, result.stderr
    data = json.loads(result.stdout)
    assert data["provable"]["cve_signatures"]["value"] == 324593


# --------------------------------------------------------------------------
# drift-check.py tests
# --------------------------------------------------------------------------
def _load_drift():
    return _load("drift_check", "drift-check.py")


def test_site_drift_detects_wrong_cve_and_scanner():
    dc = _load_drift()
    gc = _load("gen_claims", "gen-claims.py")
    register = gc.build_claims()
    # Synthetic page with the OLD wrong numbers: 327K CVEs, 15+ scanners, 30 frameworks.
    bad_html = (
        "<p>327K+ CVE Signatures</p>"
        "<p>15+ integrated scanners</p>"
        "<p>30 compliance frameworks</p>"
    )
    assert dc.check_site_drift(register, bad_html) is True


def test_site_drift_passes_when_site_matches():
    dc = _load_drift()
    gc = _load("gen_claims", "gen-claims.py")
    register = gc.build_claims()
    cve_round = register["provable"]["cve_signatures"]["value"] // 1000
    sc = register["provable"]["scanner_count"]["value"]
    fw = register["provable"]["framework_count"]["value"]
    good_html = (
        f"<p>{cve_round}K+ CVE Signatures</p>"
        f"<p>{sc} integrated scanners</p>"
        f"<p>{fw} compliance frameworks</p>"
    )
    assert dc.check_site_drift(register, good_html) is False


def test_product_drift_false_when_register_fresh():
    dc = _load_drift()
    gc = _load("gen_claims", "gen-claims.py")
    register = gc.build_claims()
    assert dc.check_product_drift(register) is False


def test_product_drift_true_when_register_stale():
    dc = _load_drift()
    gc = _load("gen_claims", "gen-claims.py")
    register = gc.build_claims()
    # Corrupt a provable value to simulate a stale register.
    register["provable"]["scanner_count"]["value"] = 99
    assert dc.check_product_drift(register) is True
