#!/usr/bin/env python3
"""
test_verdict_sanity.py - Unit tests for verdict sanity gates

WO-TRUTHY-METRICS-01: Verify that compute_verdict produces sane outputs
and properly flags implausible results.

Run:
    python -m pytest tests/test_verdict_sanity.py -v
    python tests/test_verdict_sanity.py  # Direct execution
"""

import sys
from pathlib import Path

# Add project root to path
PROJECT_ROOT = Path(__file__).parent.parent
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

# Import after path setup
from tools.bench_coldstart_memory import ColdStartSummary, compute_verdict


def test_verdict_helix_wins_memory():
    """Test verdict when helix uses less memory than baseline."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=4000,
        rss_after_load_mb=4000,
        bytes_read_mb=4000,
        time_to_first_token_ms=1000,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=4000,
        rss_after_load_mb=1000,  # 4x less memory
        bytes_read_mb=2000,       # 2x less I/O
        time_to_first_token_ms=800,  # Also faster
    )

    verdict = compute_verdict(baseline, helix)

    # Check ratios
    assert verdict["rss_ratio"] == 0.25, f"Expected rss_ratio=0.25, got {verdict['rss_ratio']}"
    assert verdict["rss_winner"] == "helix"
    assert verdict["io_ratio"] == 0.5
    assert verdict["io_winner"] == "helix"
    assert verdict["ttft_ratio"] == 0.8
    assert verdict["ttft_winner"] == "helix"
    assert verdict["status"] == "VALID"


def test_verdict_baseline_wins_memory():
    """Test verdict when baseline uses less memory than helix."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=1000,
        rss_after_load_mb=1000,
        bytes_read_mb=1000,
        time_to_first_token_ms=500,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=1000,
        rss_after_load_mb=4000,  # 4x more memory
        bytes_read_mb=3000,       # 3x more I/O
        time_to_first_token_ms=1000,  # 2x slower
    )

    verdict = compute_verdict(baseline, helix)

    # Check ratios
    assert verdict["rss_ratio"] == 4.0, f"Expected rss_ratio=4.0, got {verdict['rss_ratio']}"
    assert verdict["rss_winner"] == "baseline"
    assert verdict["io_ratio"] == 3.0
    assert verdict["io_winner"] == "baseline"
    assert verdict["ttft_ratio"] == 2.0
    assert verdict["ttft_winner"] == "baseline"
    assert verdict["status"] == "VALID"

    # Percentage should be clamped
    assert verdict.get("rss_change_clamped") == True, "Expected rss_change_clamped=True for 4x increase"
    assert verdict["rss_change_pct"] == -100, "Expected clamped to -100%"


def test_verdict_sanity_gate_triggers():
    """Test that sanity gate triggers for implausible ratios (>10x or <0.1x)."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=100,
        rss_after_load_mb=100,
        bytes_read_mb=100,
        time_to_first_token_ms=100,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=100,
        rss_after_load_mb=5000,  # 50x more memory - implausible!
        bytes_read_mb=100,
        time_to_first_token_ms=100,
    )

    verdict = compute_verdict(baseline, helix)

    assert verdict["status"] == "REVIEW_REQUIRED", f"Expected REVIEW_REQUIRED, got {verdict['status']}"
    assert "sanity_warning" in verdict
    assert verdict["rss_ratio"] == 50.0


def test_verdict_formula_inputs_stored():
    """Test that raw formula inputs are stored for audit."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=1000,
        rss_after_load_mb=1234.5,
        bytes_read_mb=567.8,
        time_to_first_token_ms=999.9,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=1000,
        rss_after_load_mb=432.1,
        bytes_read_mb=234.5,
        time_to_first_token_ms=888.8,
    )

    verdict = compute_verdict(baseline, helix)

    assert "formula_inputs" in verdict
    inputs = verdict["formula_inputs"]
    assert inputs["baseline_rss_after_load_mb"] == 1234.5
    assert inputs["helix_rss_after_load_mb"] == 432.1
    assert inputs["baseline_bytes_read_mb"] == 567.8
    assert inputs["helix_bytes_read_mb"] == 234.5
    assert inputs["baseline_ttft_ms"] == 999.9
    assert inputs["helix_ttft_ms"] == 888.8


def test_verdict_equal_values():
    """Test verdict when baseline and helix are identical."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=1000,
        rss_after_load_mb=1000,
        bytes_read_mb=500,
        time_to_first_token_ms=100,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=1000,
        rss_after_load_mb=1000,  # Same
        bytes_read_mb=500,        # Same
        time_to_first_token_ms=100,  # Same
    )

    verdict = compute_verdict(baseline, helix)

    assert verdict["rss_ratio"] == 1.0
    assert verdict["io_ratio"] == 1.0
    assert verdict["ttft_ratio"] == 1.0
    # Winner is baseline when equal (ratio >= 1.0)
    assert verdict["rss_winner"] == "baseline"
    assert verdict["status"] == "VALID"


def test_verdict_zero_baseline_values():
    """Test verdict gracefully handles zero baseline values."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=0,
        rss_after_load_mb=0,  # Zero!
        bytes_read_mb=0,
        time_to_first_token_ms=0,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=1000,
        rss_after_load_mb=1000,
        bytes_read_mb=500,
        time_to_first_token_ms=100,
    )

    # Should not raise exception
    verdict = compute_verdict(baseline, helix)

    # Should not have ratio fields when baseline is zero (avoid div by zero)
    assert "rss_ratio" not in verdict or verdict.get("rss_ratio") is None or baseline.rss_after_load_mb == 0
    assert verdict["status"] == "VALID"  # No sanity warning for missing data


def test_verdict_note_generation():
    """Test that human-readable notes are generated."""
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=4000,
        rss_after_load_mb=4000,
        bytes_read_mb=4000,
        time_to_first_token_ms=1000,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=4000,
        rss_after_load_mb=2000,  # Half memory
        bytes_read_mb=1000,       # Quarter I/O
        time_to_first_token_ms=500,  # Half TTFT
    )

    verdict = compute_verdict(baseline, helix)

    assert "note" in verdict
    assert len(verdict["note"]) > 0
    # Note should mention helix wins since all ratios < 1
    assert "Helix" in verdict["note"] or "helix" in verdict["note"]


def test_verdict_clamping_extreme_values():
    """Test that extreme percentage values are clamped to [-100, +100]."""
    # Extreme case: helix uses 12x more memory (would be -1100% without clamping)
    baseline = ColdStartSummary(
        strategy="baseline",
        model_size_mb=386,
        rss_after_load_mb=386,
        bytes_read_mb=100,
        time_to_first_token_ms=100,
    )
    helix = ColdStartSummary(
        strategy="helix",
        model_size_mb=386,
        rss_after_load_mb=4748,  # 12.3x more - this was the -1129% bug
        bytes_read_mb=100,
        time_to_first_token_ms=100,
    )

    verdict = compute_verdict(baseline, helix)

    # Raw percentage should be stored
    assert "rss_change_pct_raw" in verdict
    raw_pct = verdict["rss_change_pct_raw"]
    assert raw_pct < -100, f"Expected raw percentage < -100, got {raw_pct}"

    # Display percentage should be clamped
    assert verdict["rss_change_pct"] == -100, f"Expected clamped to -100, got {verdict['rss_change_pct']}"
    assert verdict["rss_change_clamped"] == True

    # Ratio should still be accurate
    expected_ratio = 4748 / 386
    assert abs(verdict["rss_ratio"] - expected_ratio) < 0.01


if __name__ == "__main__":
    # Run tests directly
    import traceback

    tests = [
        test_verdict_helix_wins_memory,
        test_verdict_baseline_wins_memory,
        test_verdict_sanity_gate_triggers,
        test_verdict_formula_inputs_stored,
        test_verdict_equal_values,
        test_verdict_zero_baseline_values,
        test_verdict_note_generation,
        test_verdict_clamping_extreme_values,
    ]

    passed = 0
    failed = 0

    for test in tests:
        try:
            test()
            print(f"PASS: {test.__name__}")
            passed += 1
        except AssertionError as e:
            print(f"FAIL: {test.__name__}")
            print(f"  {e}")
            failed += 1
        except Exception as e:
            print(f"ERROR: {test.__name__}")
            traceback.print_exc()
            failed += 1

    print(f"\n{'='*50}")
    print(f"Results: {passed} passed, {failed} failed")
    sys.exit(0 if failed == 0 else 1)
