"""
Tests for insaits/models.py - Foundation types
"""

import pytest
from insa_its.models import (
    Severity,
    AnomalyResult,
    MonitorResult,
    severity_from_string,
    max_severity,
    convert_legacy_anomaly,
    build_monitor_result,
    _infer_confidence,
)


# ============================================
# Severity Enum
# ============================================

class TestSeverity:
    def test_severity_values(self):
        assert Severity.INFO.value == "info"
        assert Severity.LOW.value == "low"
        assert Severity.MEDIUM.value == "medium"
        assert Severity.HIGH.value == "high"
        assert Severity.CRITICAL.value == "critical"

    def test_severity_is_string(self):
        assert isinstance(Severity.HIGH, str)
        assert Severity.HIGH == "high"


class TestSeverityFromString:
    def test_valid_conversions(self):
        assert severity_from_string("low") == Severity.LOW
        assert severity_from_string("medium") == Severity.MEDIUM
        assert severity_from_string("high") == Severity.HIGH
        assert severity_from_string("critical") == Severity.CRITICAL
        assert severity_from_string("info") == Severity.INFO

    def test_case_insensitive(self):
        assert severity_from_string("HIGH") == Severity.HIGH
        assert severity_from_string("Critical") == Severity.CRITICAL

    def test_unknown_defaults_to_medium(self):
        assert severity_from_string("unknown") == Severity.MEDIUM
        assert severity_from_string("") == Severity.MEDIUM


class TestMaxSeverity:
    def test_empty_list_returns_info(self):
        assert max_severity([]) == Severity.INFO

    def test_single_element(self):
        assert max_severity([Severity.HIGH]) == Severity.HIGH

    def test_multiple_returns_highest(self):
        assert max_severity([Severity.LOW, Severity.HIGH, Severity.MEDIUM]) == Severity.HIGH
        assert max_severity([Severity.INFO, Severity.CRITICAL]) == Severity.CRITICAL


# ============================================
# AnomalyResult
# ============================================

class TestAnomalyResult:
    def test_creation(self):
        ar = AnomalyResult(
            type="SEMANTIC_DRIFT",
            detected=True,
            confidence=0.85,
            severity=Severity.HIGH,
            description="Drift detected",
        )
        assert ar.type == "SEMANTIC_DRIFT"
        assert ar.detected is True
        assert ar.confidence == 0.85
        assert ar.severity == Severity.HIGH
        assert ar.description == "Drift detected"
        assert ar.evidence == {}
        assert ar.suggested_action == "log"

    def test_frozen(self):
        ar = AnomalyResult(
            type="TEST", detected=True, confidence=0.5,
            severity=Severity.LOW, description="test",
        )
        with pytest.raises(AttributeError):
            ar.type = "CHANGED"

    def test_to_dict(self):
        ar = AnomalyResult(
            type="JARGON_DRIFT",
            detected=True,
            confidence=0.7,
            severity=Severity.MEDIUM,
            description="Jargon drift detected",
            suggested_action="alert",
        )
        d = ar.to_dict()
        assert d["type"] == "JARGON_DRIFT"
        assert d["detected"] is True
        assert d["confidence"] == 0.7
        assert d["severity"] == "medium"
        assert d["suggested_action"] == "alert"

    def test_with_evidence(self):
        ar = AnomalyResult(
            type="TEST", detected=True, confidence=0.5,
            severity=Severity.LOW, description="test",
            evidence={"score": 0.42, "threshold": 0.3},
        )
        assert ar.evidence["score"] == 0.42


# ============================================
# MonitorResult
# ============================================

class TestMonitorResult:
    def _make_result(self, anomalies=None, severity=Severity.INFO):
        if anomalies is None:
            anomalies = []
        clean = len([a for a in anomalies if a.detected]) == 0
        return MonitorResult(
            message_id="msg-123",
            sender_id="agent1",
            receiver_id="agent2",
            timestamp="2025-01-01T00:00:00Z",
            anomalies=anomalies,
            clean=clean,
            highest_severity=severity,
            processing_ms=42.5,
        )

    def test_clean_result(self):
        result = self._make_result()
        assert result.clean is True
        assert result.should_halt() is False
        assert result.should_alert() is False
        assert result.highest_severity == Severity.INFO

    def test_should_halt_on_critical(self):
        anomaly = AnomalyResult(
            type="TEST", detected=True, confidence=0.95,
            severity=Severity.CRITICAL, description="Critical",
        )
        result = self._make_result([anomaly], Severity.CRITICAL)
        assert result.should_halt() is True
        assert result.should_alert() is True

    def test_should_alert_on_high(self):
        anomaly = AnomalyResult(
            type="TEST", detected=True, confidence=0.8,
            severity=Severity.HIGH, description="High",
        )
        result = self._make_result([anomaly], Severity.HIGH)
        assert result.should_halt() is False
        assert result.should_alert() is True

    def test_no_alert_on_medium(self):
        anomaly = AnomalyResult(
            type="TEST", detected=True, confidence=0.6,
            severity=Severity.MEDIUM, description="Medium",
        )
        result = self._make_result([anomaly], Severity.MEDIUM)
        assert result.should_halt() is False
        assert result.should_alert() is False

    def test_to_dict(self):
        anomaly = AnomalyResult(
            type="DRIFT", detected=True, confidence=0.8,
            severity=Severity.HIGH, description="Drift",
        )
        result = self._make_result([anomaly], Severity.HIGH)
        d = result.to_dict()
        assert d["message_id"] == "msg-123"
        assert d["sender_id"] == "agent1"
        assert d["clean"] is False
        assert d["highest_severity"] == "high"
        assert len(d["anomalies"]) == 1
        assert d["processing_ms"] == 42.5

    def test_to_dict_excludes_undetected(self):
        detected = AnomalyResult(
            type="DRIFT", detected=True, confidence=0.8,
            severity=Severity.HIGH, description="D",
        )
        undetected = AnomalyResult(
            type="CHAIN", detected=False, confidence=0.2,
            severity=Severity.LOW, description="U",
        )
        result = self._make_result([detected, undetected], Severity.HIGH)
        d = result.to_dict()
        assert len(d["anomalies"]) == 1
        assert d["anomalies"][0]["type"] == "DRIFT"

    def test_frozen(self):
        result = self._make_result()
        with pytest.raises(AttributeError):
            result.clean = False


# ============================================
# Legacy Conversion
# ============================================

class TestConvertLegacyAnomaly:
    def test_basic_conversion(self):
        class FakeAnomaly:
            type = "LLM_FINGERPRINT_MISMATCH"
            severity = "medium"
            details = {"expected": 40, "actual": 100, "deviation": 60.0}
            llm_id = "gpt-4"
            agent_id = "agent1"
            timestamp = 1000.0

        legacy = FakeAnomaly()
        result = convert_legacy_anomaly(legacy)
        assert result.type == "LLM_FINGERPRINT_MISMATCH"
        assert result.detected is True
        assert result.severity == Severity.MEDIUM
        assert result.confidence == 0.6  # medium base
        assert "deviation" in result.evidence

    def test_explicit_confidence_in_details(self):
        class FakeAnomaly:
            type = "TEST"
            severity = "high"
            details = {"confidence": 0.92}

        result = convert_legacy_anomaly(FakeAnomaly())
        assert result.confidence == 0.92

    def test_critical_severity(self):
        class FakeAnomaly:
            type = "HALLUCINATION_CHAIN"
            severity = "critical"
            details = {}

        result = convert_legacy_anomaly(FakeAnomaly())
        assert result.severity == Severity.CRITICAL
        assert result.suggested_action == "halt"


class TestBuildMonitorResult:
    def test_empty_anomalies(self):
        result = build_monitor_result(
            message_id="msg-1",
            sender_id="a1",
            receiver_id="a2",
            timestamp="2025-01-01T00:00:00Z",
            legacy_anomalies=[],
            processing_ms=10.0,
        )
        assert result.clean is True
        assert result.highest_severity == Severity.INFO
        assert result.anomalies == []

    def test_with_anomalies(self):
        class FakeAnomaly:
            type = "SEMANTIC_DRIFT"
            severity = "high"
            details = {"score": 0.8}

        result = build_monitor_result(
            message_id="msg-2",
            sender_id="a1",
            receiver_id="a2",
            timestamp="2025-01-01T00:00:00Z",
            legacy_anomalies=[FakeAnomaly()],
            processing_ms=25.0,
        )
        assert result.clean is False
        assert result.highest_severity == Severity.HIGH
        assert len(result.anomalies) == 1
