"""Tests for suggestion generators."""

import json
from pathlib import Path

from agentreflect.models import FailureRecord, RuleSuggestion
from agentreflect.generators.pattern_generator import PatternGenerator
from agentreflect.parsers.notes_parser import NotesParser


class TestPatternGenerator:
    def setup_method(self) -> None:
        self.gen = PatternGenerator()
        self.notes_parser = NotesParser()

    def _record(self, type_: str, message: str = "test message") -> FailureRecord:
        return FailureRecord(type=type_, message=message)

    def _suggestions_from_note(self, note: str) -> list[RuleSuggestion]:
        records = self.notes_parser.parse(note)
        return self.gen.generate(records)

    def _operational_notes(self) -> dict[str, str]:
        fixture_path = Path(__file__).parent / "fixtures" / "operational-notes.json"
        return json.loads(fixture_path.read_text())

    def test_generates_suggestions_from_attributeerror(self):
        records = [self._record("AttributeError", "'NoneType' has no attribute 'x'")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1
        texts = " ".join(s.text for s in suggestions)
        assert "None" in texts or "attribute" in texts.lower() or "hasattr" in texts

    def test_generates_suggestions_from_nameerror(self):
        records = [self._record("NameError", "name 'x' is not defined")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1

    def test_generates_suggestions_from_importerror(self):
        records = [self._record("ImportError", "cannot import 'foo'")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1
        texts = " ".join(s.text for s in suggestions)
        assert "import" in texts.lower() or "dependency" in texts.lower()

    def test_generates_suggestions_from_timeout(self):
        records = [self._record("TimeoutError", "timed out")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1
        texts = " ".join(s.text for s in suggestions)
        assert "timeout" in texts.lower()

    def test_generates_suggestions_from_keyerror(self):
        records = [self._record("KeyError", "'missing_key'")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1
        texts = " ".join(s.text for s in suggestions)
        assert ".get(" in texts or "key" in texts.lower()

    def test_generates_suggestions_from_git_fix(self):
        records = [self._record("git-fix", "fix null pointer")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1

    def test_generates_suggestions_from_note_null(self):
        records = [self._record("note-null-check", "agent forgot None check")]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 1

    def test_empty_records_returns_generic_suggestions(self):
        suggestions = self.gen.generate([])
        assert len(suggestions) >= 1

    def test_no_duplicate_texts(self):
        records = [
            self._record("AttributeError", "msg1"),
            self._record("AttributeError", "msg2"),
        ]
        suggestions = self.gen.generate(records)
        texts = [s.text for s in suggestions]
        assert len(texts) == len(set(texts))

    def test_sorted_by_confidence(self):
        records = [self._record("AttributeError"), self._record("TimeoutError")]
        suggestions = self.gen.generate(records)
        confidences = [s.confidence for s in suggestions]
        assert confidences == sorted(confidences, reverse=True)

    def test_suggestion_has_required_fields(self):
        records = [self._record("NameError")]
        suggestions = self.gen.generate(records)
        for s in suggestions:
            assert isinstance(s.category, str)
            assert isinstance(s.text, str)
            assert 0.0 <= s.confidence <= 1.0
            assert s.text != ""

    def test_rule_suggestion_markdown_item(self):
        s = RuleSuggestion(category="test", text="Do the thing", confidence=0.9)
        assert s.as_markdown_item() == "- [ ] Do the thing"

    def test_generates_from_multiple_record_types(self):
        records = [
            self._record("AttributeError"),
            self._record("TimeoutError"),
            self._record("ImportError"),
        ]
        suggestions = self.gen.generate(records)
        assert len(suggestions) >= 3

    def test_min_confidence_filter_via_generate(self):
        records = [self._record("note-general")]
        suggestions = self.gen.generate(records)
        # All suggestions should have confidence > 0
        assert all(s.confidence > 0 for s in suggestions)

    def test_exact_facts_before_fuzzy_retrieval_note_is_operational(self):
        note = self._operational_notes()["exact_facts_before_fuzzy_retrieval"]
        suggestions = self._suggestions_from_note(note)
        texts = " ".join(s.text.lower() for s in suggestions)
        assert "fuzzy retrieval" in texts
        assert "exact-match" in texts
        assert "handles" in texts
        assert "contact methods" in texts
        assert "account names" in texts
        assert "named rules" in texts
        assert "regression test" not in texts
        assert "logging" not in texts

    def test_irreversible_actions_note_requires_downstream_verification(self):
        note = self._operational_notes()["verify_downstream_state_after_irreversible_actions"]
        suggestions = self._suggestions_from_note(note)
        texts = " ".join(s.text.lower() for s in suggestions)
        assert "irreversible action" in texts
        assert "downstream system of record" in texts
        assert "reporting success" in texts
        assert "regression test" not in texts
        assert "logging" not in texts

    def test_blocked_on_human_note_consolidates_queue_after_threshold(self):
        note = self._operational_notes()["consolidate_blocked_on_human_queues_after_threshold"]
        suggestions = self._suggestions_from_note(note)
        texts = " ".join(s.text.lower() for s in suggestions)
        assert "blocked on a human" in texts
        assert "follow-up threshold" in texts
        assert "single review queue" in texts
        assert "one-off pings" in texts
        assert "regression test" not in texts
        assert "logging" not in texts

    def test_routing_note_uses_exact_target_not_inferred_recipient(self):
        note = self._operational_notes()["use_exact_routing_target_instead_of_inferred_recipient"]
        suggestions = self._suggestions_from_note(note)
        texts = " ".join(s.text.lower() for s in suggestions)
        assert "exact named handle" in texts or "exact named handle, team, queue, or tool target" in texts
        assert "do not infer the recipient" in texts
        assert "source material" in texts
        assert "regression test" not in texts
        assert "logging" not in texts
