"""
═══════════════════════════════════════════════════════════════════════════════
 ARGUS — Tests for validate_risk_scoring.py
 Validates risk scoring validation logic
═══════════════════════════════════════════════════════════════════════════════
"""

import pytest
import yaml
from pathlib import Path

from validate_risk_scoring import (
    validate_risk_scoring,
    load_context_profiles,
    VALID_MULTIPLIER_SOURCES,
    CRITICAL_THRESHOLD,
    HIGH_THRESHOLD,
    SCORE_TOLERANCE,
)

ROOT = Path(__file__).resolve().parent.parent


# ─── Helpers ────────────────────────────────────────────────────────────────

def _write_analysis(tmp_path, data, filename="analysis.yaml"):
    p = tmp_path / filename
    p.write_text(yaml.dump(data, allow_unicode=True), encoding="utf-8")
    return p


def _make_profiles(*profile_defs):
    """Build a profiles dict from (id, multipliers) tuples."""
    return {pid: mults for pid, mults in profile_defs}


GENERIC_PROFILE = _make_profiles(
    ("CTX-GENERIC", {"data_integrity": 1.0, "security": 1.0, "performance": 1.0, "regression": 1.0, "api": 1.0}),
)

FINTECH_PROFILE = _make_profiles(
    ("CTX-FINTECH", {"data_integrity": 3.0, "security": 2.5, "performance": 1.5, "regression": 2.0, "api": 2.0}),
    ("CTX-GENERIC", {"data_integrity": 1.0, "security": 1.0, "performance": 1.0, "regression": 1.0, "api": 1.0}),
)


# ═══════════════════════════════════════════════════════════════════════════════
#  VALID CASES — No errors expected
# ═══════════════════════════════════════════════════════════════════════════════

class TestValidCases:
    """Analyses with correct risk_scoring pass validation."""

    def test_no_risk_scoring_section(self, tmp_path):
        """Files without risk_scoring are silently accepted."""
        data = {"id": "ANL-20260308-001", "quick_summary": {"one_liner": "test", "status": "✅", "next_action": "none"}}
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert errors == []

    def test_valid_single_finding(self, tmp_path):
        """Single correctly scored finding passes."""
        data = {
            "id": "ANL-20260308-001",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {
                        "finding_id": "F001",
                        "base_severity": 5,
                        "multiplier_applied": 1.0,
                        "multiplier_source": "regression",
                        "confidence": 0.85,
                        "final_score": 4.25,
                        "rank": 1,
                    }
                ],
                "summary": {
                    "max_score": 4.25,
                    "avg_score": 4.25,
                    "critical_count": 0,
                    "high_count": 0,
                    "total_scored": 1,
                },
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert errors == []

    def test_valid_multiple_findings_fintech(self, tmp_path):
        """Multiple findings with fintech multipliers pass."""
        data = {
            "id": "ANL-20260308-002",
            "risk_scoring": {
                "context_profile": "CTX-FINTECH",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {
                        "finding_id": "F001",
                        "base_severity": 9,
                        "multiplier_applied": 2.5,
                        "multiplier_source": "security",
                        "confidence": 0.9,
                        "final_score": 20.25,
                        "rank": 1,
                    },
                    {
                        "finding_id": "F002",
                        "base_severity": 5,
                        "multiplier_applied": 2.0,
                        "multiplier_source": "regression",
                        "confidence": 0.7,
                        "final_score": 7.0,
                        "rank": 2,
                    },
                ],
                "summary": {
                    "max_score": 20.25,
                    "avg_score": 13.625,
                    "critical_count": 1,
                    "high_count": 1,
                    "total_scored": 2,
                },
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, FINTECH_PROFILE)
        assert errors == []

    def test_empty_findings_scored(self, tmp_path):
        """Empty findings_scored list is valid."""
        data = {
            "id": "ANL-20260308-003",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [],
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert errors == []


# ═══════════════════════════════════════════════════════════════════════════════
#  INVALID CONTEXT PROFILE
# ═══════════════════════════════════════════════════════════════════════════════

class TestInvalidContextProfile:
    """Detects unknown context profiles."""

    def test_unknown_profile_id(self, tmp_path):
        data = {
            "id": "ANL-20260308-004",
            "risk_scoring": {
                "context_profile": "CTX-UNKNOWN",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [],
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert len(errors) == 1
        assert "CTX-UNKNOWN" in errors[0]
        assert "not found" in errors[0]


# ═══════════════════════════════════════════════════════════════════════════════
#  INVALID MULTIPLIER SOURCE
# ═══════════════════════════════════════════════════════════════════════════════

class TestInvalidMultiplierSource:
    """Detects invalid multiplier_source values."""

    def test_invalid_source_key(self, tmp_path):
        data = {
            "id": "ANL-20260308-005",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {
                        "finding_id": "F001",
                        "base_severity": 5,
                        "multiplier_applied": 1.0,
                        "multiplier_source": "unknown_category",
                        "confidence": 0.8,
                        "final_score": 4.0,
                        "rank": 1,
                    }
                ],
                "summary": {"max_score": 4.0, "avg_score": 4.0, "critical_count": 0, "high_count": 0, "total_scored": 1},
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert any("multiplier_source" in e and "unknown_category" in e for e in errors)


# ═══════════════════════════════════════════════════════════════════════════════
#  MULTIPLIER MISMATCH
# ═══════════════════════════════════════════════════════════════════════════════

class TestMultiplierMismatch:
    """Detects when multiplier_applied doesn't match the profile value."""

    def test_wrong_multiplier_value(self, tmp_path):
        """Fintech security is 2.5, using 1.0 should error."""
        data = {
            "id": "ANL-20260308-006",
            "risk_scoring": {
                "context_profile": "CTX-FINTECH",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {
                        "finding_id": "F001",
                        "base_severity": 8,
                        "multiplier_applied": 1.0,  # Wrong: should be 2.5
                        "multiplier_source": "security",
                        "confidence": 0.9,
                        "final_score": 7.2,
                        "rank": 1,
                    }
                ],
                "summary": {"max_score": 7.2, "avg_score": 7.2, "critical_count": 0, "high_count": 0, "total_scored": 1},
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, FINTECH_PROFILE)
        assert any("multiplier_applied=1.0" in e and "CTX-FINTECH.security=2.5" in e for e in errors)


# ═══════════════════════════════════════════════════════════════════════════════
#  SCORE CALCULATION ERRORS
# ═══════════════════════════════════════════════════════════════════════════════

class TestScoreCalculation:
    """Detects incorrect final_score computations."""

    def test_wrong_final_score(self, tmp_path):
        data = {
            "id": "ANL-20260308-007",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {
                        "finding_id": "F001",
                        "base_severity": 8,
                        "multiplier_applied": 1.0,
                        "multiplier_source": "security",
                        "confidence": 0.9,
                        "final_score": 99.0,  # Wrong: should be 7.2
                        "rank": 1,
                    }
                ],
                "summary": {"max_score": 99.0, "avg_score": 99.0, "critical_count": 1, "high_count": 1, "total_scored": 1},
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert any("final_score=99.0" in e for e in errors)

    @pytest.mark.parametrize("base,mult,conf,expected_score", [
        (10, 2.5, 1.0, 25.0),
        (1, 1.0, 0.5, 0.5),
        (5, 3.0, 0.0, 0.0),
    ])
    def test_correct_calculations_bva(self, tmp_path, base, mult, conf, expected_score):
        """Boundary value analysis on score formula."""
        data = {
            "id": "ANL-20260308-BVA",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {
                        "finding_id": "F001",
                        "base_severity": base,
                        "multiplier_applied": mult,
                        "multiplier_source": "security",
                        "confidence": conf,
                        "final_score": expected_score,
                        "rank": 1,
                    }
                ],
                "summary": {
                    "max_score": expected_score,
                    "avg_score": expected_score,
                    "critical_count": 1 if expected_score >= CRITICAL_THRESHOLD else 0,
                    "high_count": 1 if expected_score >= HIGH_THRESHOLD else 0,
                    "total_scored": 1,
                },
            },
        }
        p = _write_analysis(tmp_path, data)
        # Use a profile where security=mult so multiplier matches
        profiles = _make_profiles(("CTX-GENERIC", {"data_integrity": 1.0, "security": mult, "performance": 1.0, "regression": 1.0, "api": 1.0}))
        errors = validate_risk_scoring(p, profiles)
        assert errors == []


# ═══════════════════════════════════════════════════════════════════════════════
#  RANK VALIDATION
# ═══════════════════════════════════════════════════════════════════════════════

class TestRankValidation:
    """Detects non-sequential or duplicate ranks."""

    def test_duplicate_ranks(self, tmp_path):
        data = {
            "id": "ANL-20260308-008",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {"finding_id": "F001", "base_severity": 5, "multiplier_applied": 1.0,
                     "multiplier_source": "security", "confidence": 0.8, "final_score": 4.0, "rank": 1},
                    {"finding_id": "F002", "base_severity": 3, "multiplier_applied": 1.0,
                     "multiplier_source": "regression", "confidence": 0.7, "final_score": 2.1, "rank": 1},
                ],
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert any("ranks" in e for e in errors)

    def test_gap_in_ranks(self, tmp_path):
        data = {
            "id": "ANL-20260308-009",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {"finding_id": "F001", "base_severity": 5, "multiplier_applied": 1.0,
                     "multiplier_source": "security", "confidence": 0.8, "final_score": 4.0, "rank": 1},
                    {"finding_id": "F002", "base_severity": 3, "multiplier_applied": 1.0,
                     "multiplier_source": "regression", "confidence": 0.7, "final_score": 2.1, "rank": 3},
                ],
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert any("ranks" in e for e in errors)


# ═══════════════════════════════════════════════════════════════════════════════
#  SUMMARY CONSISTENCY
# ═══════════════════════════════════════════════════════════════════════════════

class TestSummaryConsistency:
    """Detects inconsistencies between findings_scored and summary."""

    def test_wrong_total_scored(self, tmp_path):
        data = {
            "id": "ANL-20260308-010",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {"finding_id": "F001", "base_severity": 5, "multiplier_applied": 1.0,
                     "multiplier_source": "security", "confidence": 0.8, "final_score": 4.0, "rank": 1},
                ],
                "summary": {"max_score": 4.0, "avg_score": 4.0, "critical_count": 0, "high_count": 0, "total_scored": 5},
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert any("total_scored=5" in e for e in errors)

    def test_wrong_critical_count(self, tmp_path):
        """Score 20.25 >= 15.0 → critical_count should be 1."""
        data = {
            "id": "ANL-20260308-011",
            "risk_scoring": {
                "context_profile": "CTX-FINTECH",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {"finding_id": "F001", "base_severity": 9, "multiplier_applied": 2.5,
                     "multiplier_source": "security", "confidence": 0.9, "final_score": 20.25, "rank": 1},
                ],
                "summary": {"max_score": 20.25, "avg_score": 20.25, "critical_count": 0, "high_count": 1, "total_scored": 1},
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, FINTECH_PROFILE)
        assert any("critical_count=0" in e for e in errors)

    def test_wrong_max_score(self, tmp_path):
        data = {
            "id": "ANL-20260308-012",
            "risk_scoring": {
                "context_profile": "CTX-GENERIC",
                "scoring_method": "base × multiplier × confidence",
                "findings_scored": [
                    {"finding_id": "F001", "base_severity": 5, "multiplier_applied": 1.0,
                     "multiplier_source": "security", "confidence": 0.8, "final_score": 4.0, "rank": 1},
                ],
                "summary": {"max_score": 99.0, "avg_score": 4.0, "critical_count": 0, "high_count": 0, "total_scored": 1},
            },
        }
        p = _write_analysis(tmp_path, data)
        errors = validate_risk_scoring(p, GENERIC_PROFILE)
        assert any("max_score=99.0" in e for e in errors)


# ═══════════════════════════════════════════════════════════════════════════════
#  LOAD CONTEXT PROFILES — Integration test
# ═══════════════════════════════════════════════════════════════════════════════

class TestLoadContextProfiles:
    """Real file loading from the repository."""

    def test_load_real_profiles(self):
        """Loads real context_profiles.yaml and finds all 8 profiles."""
        profiles = load_context_profiles()
        assert len(profiles) == 8
        assert "CTX-GENERIC" in profiles
        assert "CTX-FINTECH" in profiles
        assert "CTX-HEALTH" in profiles
        assert "CTX-BA-QA" in profiles
        assert "CTX-WEBAPP" in profiles

    def test_all_profiles_have_required_multipliers(self):
        profiles = load_context_profiles()
        for pid, mults in profiles.items():
            for key in VALID_MULTIPLIER_SOURCES:
                assert key in mults, f"{pid} missing multiplier '{key}'"

    def test_generic_profile_neutral(self):
        """CTX-GENERIC has all multipliers at 1.0."""
        profiles = load_context_profiles()
        for key, val in profiles["CTX-GENERIC"].items():
            assert val == 1.0, f"CTX-GENERIC.{key} should be 1.0, got {val}"


# ═══════════════════════════════════════════════════════════════════════════════
#  REAL DATA VALIDATION — Template analysis
# ═══════════════════════════════════════════════════════════════════════════════

class TestRealDataValidation:
    """Validate actual repository analysis files."""

    def test_template_valid(self):
        """The template analysis should pass risk_scoring validation."""
        template = ROOT / "memory" / "episodic" / "analyses" / "_template_analysis.yaml"
        if not template.exists():
            pytest.skip("Template not found")
        profiles = load_context_profiles()
        errors = validate_risk_scoring(template, profiles)
        assert errors == [], f"Template errors: {errors}"

    def test_all_analyses_valid(self):
        """All existing analysis files pass risk_scoring validation."""
        analyses_dir = ROOT / "memory" / "episodic" / "analyses"
        profiles = load_context_profiles()
        all_errors = []
        for f in sorted(analyses_dir.glob("*.yaml")):
            errors = validate_risk_scoring(f, profiles)
            all_errors.extend(errors)
        assert all_errors == [], f"Validation errors:\n" + "\n".join(all_errors)
