"""Tests for UncertaintyPropagationDetector."""

import pytest
from insa_its.detectors.uncertainty_propagation import (
    UncertaintyPropagationDetector,
    UNCERTAINTY_MARKERS,
    CERTAINTY_MARKERS,
)


class TestInit:
    def test_default_lookback(self):
        d = UncertaintyPropagationDetector()
        assert d.lookback == 5

    def test_custom_lookback(self):
        d = UncertaintyPropagationDetector(lookback=10)
        assert d.lookback == 10

    def test_invalid_lookback_raises(self):
        with pytest.raises(ValueError, match="lookback must be at least 1"):
            UncertaintyPropagationDetector(lookback=0)

    def test_name_and_enabled(self):
        d = UncertaintyPropagationDetector()
        assert d.name == "uncertainty_propagation"
        assert d.enabled is True


class TestCleanMessages:
    def test_no_markers_returns_clean(self):
        d = UncertaintyPropagationDetector()
        result = d.check("The weather is sunny today.", 0)
        assert result["detected"] is False
        assert result["flags"] == []
        assert result["uncertainty_markers_found"] == []
        assert result["certainty_markers_found"] == []

    def test_uncertainty_only_no_upstream(self):
        d = UncertaintyPropagationDetector()
        result = d.check("The results are partial and estimated.", 0)
        assert result["detected"] is False
        assert len(result["uncertainty_markers_found"]) > 0
        assert "partial" in [m.lower() for m in result["uncertainty_markers_found"]]

    def test_certainty_only_no_upstream(self):
        d = UncertaintyPropagationDetector()
        result = d.check("The complete list of all verified results.", 0)
        assert result["detected"] is False
        assert len(result["certainty_markers_found"]) > 0


class TestCertaintyPromotion:
    def test_detects_certainty_promotion(self):
        d = UncertaintyPropagationDetector()
        # Upstream: uncertain about user records
        d.check(
            "The query returned partial user records from the database results table.",
            0,
        )
        # Downstream: certain about same topic
        result = d.check(
            "The complete list of all user records from the database is verified and confirmed.",
            1,
        )
        assert result["detected"] is True
        assert result["upstream_uncertain"] is True
        assert any("Certainty promotion" in f for f in result["flags"])

    def test_no_promotion_when_topics_differ(self):
        d = UncertaintyPropagationDetector()
        # Upstream: uncertain about weather
        d.check("The weather forecast is approximately accurate.", 0)
        # Downstream: certain about completely different topic
        result = d.check(
            "The complete database migration is verified and confirmed.",
            1,
        )
        # Should not flag -- different topics
        assert result["upstream_uncertain"] is False


class TestUncertaintyDropped:
    def test_detects_uncertainty_dropped(self):
        d = UncertaintyPropagationDetector()
        # Upstream: uncertain about user records data table
        d.check(
            "The query returned partial user records from the data table with estimated counts.",
            0,
        )
        # Downstream: mentions same topic but no uncertainty or certainty markers
        result = d.check(
            "Processing the user records from the data table for the report output.",
            1,
        )
        # Need enough topic overlap (MIN_TOPIC_OVERLAP + 1 = 3) for dropped detection
        if result["detected"]:
            assert any("dropped" in f.lower() for f in result["flags"])


class TestInternalContradiction:
    def test_detects_contradiction_in_same_sentence(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "The results are approximately complete and definitely verified as the total count.",
            0,
        )
        assert result["detected"] is True
        assert any("Internal contradiction" in f for f in result["flags"])

    def test_no_contradiction_short_sentence(self):
        d = UncertaintyPropagationDetector()
        # Short sentences (< 20 chars) are skipped
        result = d.check("Maybe all.", 0)
        assert result["detected"] is False

    def test_contradiction_both_markers_present(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "This is possibly the definitive and complete answer to the approximately uncertain question about totals.",
            0,
        )
        assert result["detected"] is True


class TestMarkerDetection:
    def test_finds_uncertainty_markers(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "The data is partial, truncated, and approximately 500 rows.",
            0,
        )
        markers = [m.lower() for m in result["uncertainty_markers_found"]]
        assert "partial" in markers
        assert "truncated" in markers
        assert "approximately" in markers

    def test_finds_certainty_markers(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "The complete dataset shows exactly 500 verified entries.",
            0,
        )
        markers = [m.lower() for m in result["certainty_markers_found"]]
        assert "complete" in markers
        assert "exactly" in markers
        assert "verified" in markers

    def test_hedging_patterns(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "This might be the result, but we are not sure about the data count.",
            0,
        )
        markers = [m.lower() for m in result["uncertainty_markers_found"]]
        assert "might be" in markers
        assert "not sure" in markers

    def test_range_patterns(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "There are at least 100 users and at most 500 records in the sample.",
            0,
        )
        markers = [m.lower() for m in result["uncertainty_markers_found"]]
        assert "at least" in markers
        assert "at most" in markers
        assert "sample" in markers

    def test_deduplication(self):
        d = UncertaintyPropagationDetector()
        result = d.check(
            "The partial data is partial and the partial results are partial.",
            0,
        )
        # Should deduplicate "partial"
        partial_count = sum(
            1 for m in result["uncertainty_markers_found"]
            if m.lower() == "partial"
        )
        assert partial_count == 1


class TestReset:
    def test_reset_clears_history(self):
        d = UncertaintyPropagationDetector()
        d.check("Partial user records from the data table.", 0)
        d.reset()
        # After reset, no upstream context should exist
        result = d.check(
            "The complete list of all user records from the data table is confirmed.",
            1,
        )
        # Should NOT detect certainty promotion (no upstream after reset)
        assert result["upstream_uncertain"] is False


class TestLookbackWindow:
    def test_lookback_limits_history_scan(self):
        d = UncertaintyPropagationDetector(lookback=2)
        # Message 0: uncertain about user records data
        d.check(
            "The query returned partial user records from the data table results.",
            0,
        )
        # Messages 1-3: unrelated filler
        for i in range(1, 4):
            d.check(f"Processing step {i} for internal workflow.", i)
        # Message 4: certain about same topic
        result = d.check(
            "The complete list of all user records from the data table is confirmed.",
            4,
        )
        # With lookback=2, message 0 is outside the window
        assert result["upstream_uncertain"] is False

    def test_within_lookback_detects(self):
        d = UncertaintyPropagationDetector(lookback=3)
        # Message 0: uncertain about user records data
        d.check(
            "The query returned partial user records from the data table results.",
            0,
        )
        # Message 1: filler
        d.check("Processing step for internal workflow.", 1)
        # Message 2: certain about same topic
        result = d.check(
            "The complete list of all user records from the data table is confirmed.",
            2,
        )
        assert result["upstream_uncertain"] is True


class TestResultFormat:
    def test_result_has_all_keys(self):
        d = UncertaintyPropagationDetector()
        result = d.check("Some normal text about processing data.", 0)
        assert "detected" in result
        assert "uncertainty_markers_found" in result
        assert "certainty_markers_found" in result
        assert "upstream_uncertain" in result
        assert "flags" in result
        assert "description" in result

    def test_detected_description_varies(self):
        d = UncertaintyPropagationDetector()
        clean = d.check("Normal text.", 0)
        assert "No uncertainty" in clean["description"]

        flagged = d.check(
            "The results are approximately complete and definitely the total verified count of items.",
            1,
        )
        if flagged["detected"]:
            assert "failure" in flagged["description"].lower()
