"""tests/test_pipeline_adaptive_cisc.py — Unit tests for AdaptiveCISC."""
import pytest
import json

pytestmark = pytest.mark.unit


class TestImport:
    def test_import(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        assert AdaptiveCISC is not None


class TestInstantiation:
    def test_default_construction(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test")
        assert cisc.domain == "test"

    def test_default_thresholds(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test")
        high_t, low_t = cisc.get_thresholds()
        assert high_t == pytest.approx(0.50, abs=0.01)
        assert low_t == pytest.approx(0.70, abs=0.01)

    def test_custom_epsilon(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test", epsilon=0.2)
        assert cisc.epsilon == 0.2


class TestRecordOutcome:
    def _make_cisc(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        return AdaptiveCISC(domain="test", min_samples=5)

    def test_record_increments_count(self):
        cisc = self._make_cisc()
        cisc.record_outcome(risk_score=0.75, tier="LOW", was_wrong=True)
        assert cisc._stats.n_observations == 1

    def test_multiple_records(self):
        cisc = self._make_cisc()
        for _ in range(5):
            cisc.record_outcome(risk_score=0.75, tier="LOW", was_wrong=True)
        assert cisc._stats.n_observations == 5

    def test_precision_computed_after_enough_samples(self):
        cisc = self._make_cisc()
        for _ in range(5):
            cisc.record_outcome(risk_score=0.75, tier="LOW", was_wrong=True)
        assert cisc._stats.precision >= 0.0


class TestGetThresholds:
    def test_thresholds_clamped(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test", min_samples=3, epsilon=0.0)
        for _ in range(10):
            cisc.record_outcome(0.8, "LOW", True)
        high_t, low_t = cisc.get_thresholds()
        assert 0.25 <= high_t <= 0.90
        assert 0.25 <= low_t <= 0.90

    def test_low_threshold_above_high(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test")
        high_t, low_t = cisc.get_thresholds()
        assert low_t >= high_t


class TestSummary:
    def test_summary_has_domain(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test")
        s = cisc.summary()
        assert s["domain"] == "test"

    def test_summary_has_n_observations(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test")
        cisc.record_outcome(0.8, "LOW", True)
        s = cisc.summary()
        assert s["n_observations"] == 1


class TestPersistence:
    def test_no_state_dir_no_file(self):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        cisc = AdaptiveCISC(domain="test", state_dir=None)
        cisc.record_outcome(0.8, "LOW", True)
        # No error thrown
        assert cisc._stats.n_observations == 1

    def test_state_persisted_to_file(self, tmp_path):
        from llm_guard.pipeline.adaptive_cisc import AdaptiveCISC
        state_dir = str(tmp_path / "cisc_state")
        cisc = AdaptiveCISC(domain="test", state_dir=state_dir)
        cisc.record_outcome(0.8, "LOW", True)
        # Re-load — state_dir auto-saves on record_outcome
        cisc2 = AdaptiveCISC(domain="test", state_dir=state_dir)
        assert cisc2._stats.n_observations == 1
