"""
Integration tests for V3 features - end-to-end flows
"""

import os
import json
import pytest
from unittest.mock import patch, MagicMock
from insa_its.models import Severity
from insa_its.circuit_breaker import AgentCircuitBreaker
from insa_its.detectors import (
    SemanticDriftDetector,
    HallucinationChainDetector,
    JargonDriftDetector,
)
from insa_its.audit import AuditLogger
from insa_its.metrics import MetricsCollector
from insa_its.interventions import InterventionEngine


class TestDetectorChain:
    """Test that multiple detectors can run in sequence."""

    def test_all_detectors_clean(self):
        import numpy as np

        sd = SemanticDriftDetector(min_window=2)
        hc = HallucinationChainDetector()
        jd = JargonDriftDetector()

        text = "The system processes data using standard methods."
        emb = np.random.randn(384)
        emb = emb / np.linalg.norm(emb)

        drift = sd.check(emb)
        chain = hc.check(text, 1)
        jargon = jd.check(text)

        assert drift.detected is False
        assert chain["detected"] is False
        assert jargon["detected"] is False

    def test_detectors_independent(self):
        """Each detector tracks its own state independently."""
        import numpy as np

        sd = SemanticDriftDetector()
        hc = HallucinationChainDetector()
        jd = JargonDriftDetector()

        # Process multiple messages
        for i in range(5):
            emb = np.random.randn(384)
            emb = emb / np.linalg.norm(emb)
            sd.check(emb)
            hc.check(f"Message {i} with some content.", i)
            jd.check(f"Message {i} with some content.")

        # Reset one, others keep state
        sd.reset()
        emb = np.random.randn(384)
        emb = emb / np.linalg.norm(emb)
        drift = sd.check(emb)
        assert drift.window == 0  # Reset

        # Jargon still has state
        assert jd._message_count == 5


class TestCircuitBreakerWithMetrics:
    """Test circuit breaker + metrics working together."""

    def test_circuit_breaker_blocks_after_threshold(self):
        cb = AgentCircuitBreaker(window_size=5, failure_threshold=0.5)
        metrics = MetricsCollector()

        for i in range(5):
            had_anomaly = True  # All anomalous
            cb.record("risky_agent", had_anomaly)
            metrics.record_message(had_anomaly, "high", 10.0)

        assert cb.is_open("risky_agent") is True
        assert metrics.get_counter("insaits_anomalies_total", {"severity": "high"}) == 5.0

    def test_metrics_track_across_agents(self):
        cb = AgentCircuitBreaker(window_size=10, failure_threshold=0.5)
        metrics = MetricsCollector()

        for _ in range(3):
            cb.record("agent1", True)
            metrics.record_message(True, "high", 20.0)

        for _ in range(7):
            cb.record("agent2", False)
            metrics.record_message(False, "info", 5.0)

        assert metrics.get_counter("insaits_messages_total") == 10.0
        assert metrics.get_counter("insaits_anomalies_total", {"severity": "high"}) == 3.0


class TestAuditIntegrity:
    """Test audit log integrity across multiple writes."""

    def test_large_audit_log_integrity(self, tmp_path):
        audit_file = str(tmp_path / "audit.jsonl")
        audit = AuditLogger(audit_file)

        for i in range(100):
            audit.write(
                event_type="message_processed",
                sender=f"agent{i % 5}",
                receiver=f"agent{(i + 1) % 5}",
                had_anomaly=i % 7 == 0,
                severity="high" if i % 7 == 0 else "info",
                action_taken="processed",
                message_hash=f"hash{i}",
                correlation_id=f"corr-{i // 10}",
            )

        assert audit.verify_integrity() is True

    def test_audit_resume_then_verify(self, tmp_path):
        audit_file = str(tmp_path / "audit.jsonl")

        # Session 1
        audit1 = AuditLogger(audit_file)
        for i in range(10):
            audit1.write("e", "s", "r", False, "info", "p", f"h{i}")

        # Session 2 (resume)
        audit2 = AuditLogger(audit_file)
        for i in range(10, 20):
            audit2.write("e", "s", "r", True, "high", "q", f"h{i}")

        # Verify entire chain
        audit3 = AuditLogger(audit_file)
        assert audit3.verify_integrity() is True


class TestInterventionWithMonitorResult:
    """Test intervention engine with real MonitorResult objects."""

    def test_full_intervention_flow(self):
        from insa_its.models import AnomalyResult, MonitorResult

        engine = InterventionEngine()

        # Register HITL
        decisions = []

        def hitl(msg, result, ctx):
            decisions.append(result.highest_severity)
            return False  # Always deny

        engine.register_hitl_callback(hitl)
        engine.register_reroute("agent1", "backup")

        # Test critical
        critical_anomaly = AnomalyResult(
            type="HALLUCINATION_CHAIN", detected=True,
            confidence=0.95, severity=Severity.CRITICAL,
            description="Chain detected",
        )
        critical_result = MonitorResult(
            message_id="m1", sender_id="agent1", receiver_id="agent2",
            timestamp="2025-01-01T00:00:00Z",
            anomalies=[critical_anomaly], clean=False,
            highest_severity=Severity.CRITICAL, processing_ms=50.0,
        )
        outcome = engine.handle({"text": "bad"}, critical_result)
        assert outcome["action"] == "quarantined"
        assert len(decisions) == 1

        # Test high with reroute
        high_anomaly = AnomalyResult(
            type="SEMANTIC_DRIFT", detected=True,
            confidence=0.8, severity=Severity.HIGH,
            description="Drift",
        )
        high_result = MonitorResult(
            message_id="m2", sender_id="agent1", receiver_id="agent2",
            timestamp="2025-01-01T00:00:00Z",
            anomalies=[high_anomaly], clean=False,
            highest_severity=Severity.HIGH, processing_ms=30.0,
        )
        outcome = engine.handle({"text": "drift"}, high_result)
        assert outcome["action"] == "rerouted"
        assert outcome["rerouted_to"] == "backup"


class TestPrometheusEndToEnd:
    """Test that metrics render correctly after realistic usage."""

    def test_realistic_metrics(self):
        m = MetricsCollector()

        # Simulate 100 messages
        for i in range(100):
            had_anomaly = i % 10 == 0  # 10% anomaly rate
            severity = "high" if had_anomaly else "info"
            processing_ms = 20.0 + (i % 30)
            m.record_message(had_anomaly, severity, processing_ms)

        m.gauge("insaits_active_agents", 5)

        output = m.render_prometheus()

        # Verify structure
        assert "insaits_messages_total 100.0" in output
        assert "insaits_anomalies_total" in output
        assert "insaits_active_agents 5" in output
        assert "insaits_processing_duration_ms_bucket" in output
        assert "insaits_processing_duration_ms_count 100" in output

        # Verify parseable
        for line in output.strip().split("\n"):
            if line.startswith("#"):
                continue
            parts = line.rsplit(" ", 1)
            assert len(parts) == 2, f"Bad metric line: {line}"
