"""Tests for the auto-crystallizer module.

Refs: #961, #953
"""

import pytest


@pytest.mark.unit
class TestCrystallizeReasoning:
    """Tests for rule extraction from LLM reasoning chains."""

    def test_extracts_threshold_rule(self):
        """Extracts threshold-based rules from reasoning text."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        reasoning = "I chose to heal because health was below 30%. So I used a healing potion."
        rules = crystallize_reasoning(reasoning)
        assert len(rules) >= 1
        # Should detect the threshold pattern
        found = any("health" in r.condition.lower() and "30" in r.condition for r in rules)
        assert found, f"Expected threshold rule, got: {[r.condition for r in rules]}"

    def test_extracts_comparison_rule(self):
        """Extracts comparison operators from reasoning."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        reasoning = "The stamina_pct < 20 so I decided to rest."
        rules = crystallize_reasoning(reasoning)
        assert len(rules) >= 1
        found = any("stamina_pct" in r.condition and "<" in r.condition for r in rules)
        assert found, f"Expected comparison rule, got: {[r.condition for r in rules]}"

    def test_extracts_choice_reason_rule(self):
        """Extracts 'chose X because Y' patterns."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        reasoning = "I chose retreat because the enemy outnumbered us."
        rules = crystallize_reasoning(reasoning)
        assert len(rules) >= 1
        found = any(r.action == "retreat" for r in rules)
        assert found, f"Expected 'retreat' action, got: {[r.action for r in rules]}"

    def test_deduplicates_rules(self):
        """Same pattern extracted once, not twice."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        reasoning = (
            "I chose heal because health was below 30%. Again, health was below 30% so I healed."
        )
        rules = crystallize_reasoning(reasoning)
        ids = [r.id for r in rules]
        # Duplicate condition+action should produce same ID
        assert len(ids) == len(set(ids)), "Duplicate rules detected"

    def test_empty_reasoning_returns_no_rules(self):
        """Empty or unstructured text produces no rules."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        rules = crystallize_reasoning("")
        assert rules == []

        rules = crystallize_reasoning("The weather is nice today.")
        assert rules == []

    def test_rule_has_excerpt(self):
        """Extracted rules include a reasoning excerpt for provenance."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        reasoning = "I chose attack because the enemy health was below 50%."
        rules = crystallize_reasoning(reasoning)
        assert len(rules) >= 1
        assert rules[0].reasoning_excerpt != ""

    def test_context_stored_in_metadata(self):
        """Context dict is stored in rule metadata."""
        from timmy.sovereignty.auto_crystallizer import crystallize_reasoning

        context = {"game": "morrowind", "location": "balmora"}
        reasoning = "I chose to trade because gold_amount > 100."
        rules = crystallize_reasoning(reasoning, context=context)
        assert len(rules) >= 1
        assert rules[0].metadata.get("game") == "morrowind"


@pytest.mark.unit
class TestRule:
    """Tests for the Rule dataclass."""

    def test_initial_state(self):
        """New rules start with default confidence and no applications."""
        from timmy.sovereignty.auto_crystallizer import Rule

        rule = Rule(id="test", condition="hp < 30", action="heal")
        assert rule.confidence == 0.5
        assert rule.times_applied == 0
        assert rule.times_succeeded == 0
        assert not rule.is_reliable

    def test_success_rate(self):
        """Success rate is calculated correctly."""
        from timmy.sovereignty.auto_crystallizer import Rule

        rule = Rule(id="test", condition="hp < 30", action="heal")
        rule.times_applied = 10
        rule.times_succeeded = 8
        assert rule.success_rate == 0.8

    def test_is_reliable(self):
        """Rule becomes reliable with high confidence + enough applications."""
        from timmy.sovereignty.auto_crystallizer import Rule

        rule = Rule(
            id="test",
            condition="hp < 30",
            action="heal",
            confidence=0.85,
            times_applied=5,
            times_succeeded=4,
        )
        assert rule.is_reliable

    def test_not_reliable_low_confidence(self):
        """Rule is not reliable with low confidence."""
        from timmy.sovereignty.auto_crystallizer import Rule

        rule = Rule(
            id="test",
            condition="hp < 30",
            action="heal",
            confidence=0.5,
            times_applied=10,
            times_succeeded=8,
        )
        assert not rule.is_reliable


@pytest.mark.unit
class TestRuleStore:
    """Tests for the RuleStore persistence layer."""

    def test_add_and_retrieve(self, tmp_path):
        """Rules can be added and retrieved."""
        from timmy.sovereignty.auto_crystallizer import Rule, RuleStore

        store = RuleStore(path=tmp_path / "strategy.json")
        rule = Rule(id="r1", condition="hp < 30", action="heal")
        store.add(rule)

        retrieved = store.get("r1")
        assert retrieved is not None
        assert retrieved.condition == "hp < 30"

    def test_persist_and_reload(self, tmp_path):
        """Rules survive persist → reload cycle."""
        from timmy.sovereignty.auto_crystallizer import Rule, RuleStore

        path = tmp_path / "strategy.json"
        store = RuleStore(path=path)
        store.add(Rule(id="r1", condition="hp < 30", action="heal"))
        store.add(Rule(id="r2", condition="mana > 50", action="cast"))

        # Create a new store from the same file
        store2 = RuleStore(path=path)
        assert len(store2) == 2
        assert store2.get("r1") is not None
        assert store2.get("r2") is not None

    def test_record_application_success(self, tmp_path):
        """Recording a successful application boosts confidence."""
        from timmy.sovereignty.auto_crystallizer import Rule, RuleStore

        store = RuleStore(path=tmp_path / "strategy.json")
        store.add(Rule(id="r1", condition="hp < 30", action="heal", confidence=0.5))

        store.record_application("r1", succeeded=True)
        rule = store.get("r1")
        assert rule.times_applied == 1
        assert rule.times_succeeded == 1
        assert rule.confidence > 0.5

    def test_record_application_failure(self, tmp_path):
        """Recording a failed application penalizes confidence."""
        from timmy.sovereignty.auto_crystallizer import Rule, RuleStore

        store = RuleStore(path=tmp_path / "strategy.json")
        store.add(Rule(id="r1", condition="hp < 30", action="heal", confidence=0.8))

        store.record_application("r1", succeeded=False)
        rule = store.get("r1")
        assert rule.times_applied == 1
        assert rule.times_succeeded == 0
        assert rule.confidence < 0.8

    def test_add_many_counts_new(self, tmp_path):
        """add_many returns count of genuinely new rules."""
        from timmy.sovereignty.auto_crystallizer import Rule, RuleStore

        store = RuleStore(path=tmp_path / "strategy.json")
        store.add(Rule(id="r1", condition="hp < 30", action="heal"))

        new_rules = [
            Rule(id="r1", condition="hp < 30", action="heal"),  # existing
            Rule(id="r2", condition="mana > 50", action="cast"),  # new
        ]
        added = store.add_many(new_rules)
        assert added == 1
        assert len(store) == 2

    def test_find_matching_returns_reliable_only(self, tmp_path):
        """find_matching only returns rules above confidence threshold."""
        from timmy.sovereignty.auto_crystallizer import Rule, RuleStore

        store = RuleStore(path=tmp_path / "strategy.json")
        store.add(
            Rule(
                id="r1",
                condition="health low",
                action="heal",
                confidence=0.9,
                times_applied=5,
                times_succeeded=4,
            )
        )
        store.add(
            Rule(
                id="r2",
                condition="health low",
                action="flee",
                confidence=0.3,
                times_applied=1,
                times_succeeded=0,
            )
        )

        matches = store.find_matching({"health": "low"})
        assert len(matches) == 1
        assert matches[0].id == "r1"
