"""
Tests for insaits/interventions.py - Intervention engine
"""

import pytest
from unittest.mock import MagicMock
from insa_its.interventions import InterventionEngine
from insa_its.models import AnomalyResult, MonitorResult, Severity


def _make_monitor_result(severity=Severity.INFO, anomaly_types=None):
    """Create a MonitorResult for testing."""
    if anomaly_types is None:
        anomaly_types = []

    anomalies = [
        AnomalyResult(
            type=atype,
            detected=True,
            confidence=0.9,
            severity=severity,
            description=f"{atype} detected",
        )
        for atype in anomaly_types
    ]

    clean = len(anomalies) == 0
    return MonitorResult(
        message_id="msg-1",
        sender_id="agent1",
        receiver_id="agent2",
        timestamp="2025-01-01T00:00:00Z",
        anomalies=anomalies,
        clean=clean,
        highest_severity=severity if anomalies else Severity.INFO,
        processing_ms=10.0,
    )


class TestInterventionClean:
    def test_clean_message_delivered(self):
        engine = InterventionEngine()
        result = _make_monitor_result()
        outcome = engine.handle({"text": "hello"}, result)
        assert outcome["action"] == "delivered"
        assert outcome["modified"] is False


class TestInterventionCritical:
    def test_critical_quarantined_no_hitl(self):
        engine = InterventionEngine()
        result = _make_monitor_result(
            Severity.CRITICAL, ["HALLUCINATION_CHAIN"]
        )
        outcome = engine.handle({"text": "bad"}, result)
        assert outcome["action"] == "quarantined"
        assert outcome["severity"] == "critical"

    def test_critical_with_hitl_allow(self):
        engine = InterventionEngine()

        def allow_all(msg, result, ctx):
            return True

        engine.register_hitl_callback(allow_all)
        result = _make_monitor_result(
            Severity.CRITICAL, ["HALLUCINATION_CHAIN"]
        )
        outcome = engine.handle({"text": "bad"}, result)
        assert outcome["action"] == "delivered_with_warning"

    def test_critical_with_hitl_deny(self):
        engine = InterventionEngine()

        def deny_all(msg, result, ctx):
            return False

        engine.register_hitl_callback(deny_all)
        result = _make_monitor_result(
            Severity.CRITICAL, ["HALLUCINATION_CHAIN"]
        )
        outcome = engine.handle({"text": "bad"}, result)
        assert outcome["action"] == "quarantined"

    def test_critical_with_hitl_exception(self):
        engine = InterventionEngine()

        def raise_error(msg, result, ctx):
            raise RuntimeError("callback error")

        engine.register_hitl_callback(raise_error)
        result = _make_monitor_result(
            Severity.CRITICAL, ["SEMANTIC_DRIFT"]
        )
        outcome = engine.handle({"text": "bad"}, result)
        # Should quarantine on callback error
        assert outcome["action"] == "quarantined"


class TestInterventionHigh:
    def test_high_delivers_with_warning_no_reroute(self):
        engine = InterventionEngine()
        result = _make_monitor_result(Severity.HIGH, ["SEMANTIC_DRIFT"])
        outcome = engine.handle({"text": "msg"}, result)
        assert outcome["action"] == "delivered_with_warning"
        assert outcome["severity"] == "high"

    def test_high_reroutes_when_configured(self):
        engine = InterventionEngine()
        engine.register_reroute("agent1", "backup_agent")
        result = _make_monitor_result(Severity.HIGH, ["SEMANTIC_DRIFT"])
        outcome = engine.handle({"text": "msg"}, result)
        assert outcome["action"] == "rerouted"
        assert outcome["rerouted_to"] == "backup_agent"
        assert outcome["modified"] is True


class TestInterventionMedium:
    def test_medium_delivers_with_warning(self):
        engine = InterventionEngine()
        result = _make_monitor_result(Severity.MEDIUM, ["JARGON_DRIFT"])
        outcome = engine.handle({"text": "msg"}, result)
        assert outcome["action"] == "delivered_with_warning"
        assert outcome["severity"] == "medium"


class TestInterventionLow:
    def test_low_delivers(self):
        engine = InterventionEngine()
        result = _make_monitor_result(Severity.LOW, ["MINOR_ISSUE"])
        outcome = engine.handle({"text": "msg"}, result)
        assert outcome["action"] == "delivered"

    def test_info_delivers(self):
        engine = InterventionEngine()
        result = _make_monitor_result(Severity.INFO, ["NOTE"])
        outcome = engine.handle({"text": "msg"}, result)
        assert outcome["action"] == "delivered"


class TestCustomHandlers:
    def test_custom_handler_called(self):
        engine = InterventionEngine()
        called = []

        def handler(msg, anomaly, ctx):
            called.append(anomaly.type)

        engine.register_handler("SEMANTIC_DRIFT", handler)
        result = _make_monitor_result(Severity.MEDIUM, ["SEMANTIC_DRIFT"])
        engine.handle({"text": "msg"}, result)
        assert "SEMANTIC_DRIFT" in called

    def test_custom_handler_exception_handled(self):
        engine = InterventionEngine()

        def bad_handler(msg, anomaly, ctx):
            raise ValueError("handler error")

        engine.register_handler("TEST", bad_handler)
        result = _make_monitor_result(Severity.MEDIUM, ["TEST"])
        # Should not raise
        outcome = engine.handle({"text": "msg"}, result)
        assert outcome is not None
