"""tests/test_monitoring_intra_chain_drift_detector.py — Unit tests for IntraChainDriftDetector."""
import pytest

pytestmark = pytest.mark.unit

STEPS_STABLE = [
    {"thought": f"step {i}", "action_type": "Search",
     "action_arg": f"q{i}", "observation": f"long obs result {i}"}
    for i in range(10)
]
STEPS_DEGRADED = STEPS_STABLE[:3] + [
    {"thought": "", "action_type": "Search", "action_arg": "", "observation": ""}
    for _ in range(7)
]


class TestImport:
    def test_import(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        assert IntraChainDriftDetector is not None

    def test_import_result(self):
        from llm_guard.monitoring.intra_chain_drift_detector import DriftResult
        assert DriftResult is not None


class TestInstantiation:
    def test_default_params(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector()
        assert det.cusum_k == 0.1
        assert det.cusum_h == 1.0
        assert det.min_baseline_steps == 3

    def test_custom_params(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector(cusum_k=0.2, cusum_h=2.0, min_baseline_steps=5)
        assert det.min_baseline_steps == 5


class TestStepRisk:
    def test_empty_step_high_risk(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        risk = IntraChainDriftDetector._step_risk(
            {"thought": "", "action_type": "Search", "action_arg": "", "observation": ""}
        )
        assert risk > 0.3

    def test_full_step_low_risk(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        risk = IntraChainDriftDetector._step_risk(
            {"thought": "detailed reasoning", "action_type": "Search",
             "action_arg": "good query", "observation": "long relevant result"}
        )
        assert risk < 0.7


class TestDetect:
    def test_empty_steps_returns_no_drift(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector()
        result = det.detect([])
        assert not result.drift_detected
        assert result.n_steps == 0

    def test_too_few_steps_no_drift(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector(min_baseline_steps=3)
        result = det.detect(STEPS_STABLE[:2])
        assert not result.drift_detected

    def test_stable_chain_no_drift(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector(min_baseline_steps=3)
        result = det.detect(STEPS_STABLE)
        # Stable chain should not trigger drift
        assert result.n_steps == len(STEPS_STABLE)
        assert isinstance(result.drift_detected, bool)

    def test_degraded_chain_may_drift(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector(min_baseline_steps=3, cusum_h=0.5)
        result = det.detect(STEPS_DEGRADED)
        assert result.n_steps == len(STEPS_DEGRADED)

    def test_result_has_step_risks(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector()
        result = det.detect(STEPS_STABLE)
        assert len(result.step_risks) == len(STEPS_STABLE)
        assert all(0.0 <= r <= 1.0 for r in result.step_risks)

    def test_drift_point_none_if_no_drift(self):
        from llm_guard.monitoring.intra_chain_drift_detector import IntraChainDriftDetector
        det = IntraChainDriftDetector(cusum_h=100.0)  # Very high threshold → never trigger
        result = det.detect(STEPS_STABLE)
        assert not result.drift_detected
        assert result.drift_point is None
