"""Tests for the Nexus Introspection Engine."""

from unittest.mock import MagicMock, patch

from timmy.nexus.introspection import (
    CognitiveSummary,
    IntrospectionSnapshot,
    NexusIntrospector,
    SessionAnalytics,
    ThoughtSummary,
)

# ── Data model tests ─────────────────────────────────────────────────────────


class TestCognitiveSummary:
    def test_defaults(self):
        s = CognitiveSummary()
        assert s.mood == "settled"
        assert s.engagement == "idle"
        assert s.focus_topic is None

    def test_to_dict(self):
        s = CognitiveSummary(mood="curious", engagement="deep", focus_topic="architecture")
        d = s.to_dict()
        assert d["mood"] == "curious"
        assert d["engagement"] == "deep"
        assert d["focus_topic"] == "architecture"


class TestThoughtSummary:
    def test_to_dict(self):
        t = ThoughtSummary(
            id="t1", content="Hello world", seed_type="freeform", created_at="2026-01-01"
        )
        d = t.to_dict()
        assert d["id"] == "t1"
        assert d["seed_type"] == "freeform"
        assert d["parent_id"] is None


class TestSessionAnalytics:
    def test_defaults(self):
        a = SessionAnalytics()
        assert a.total_messages == 0
        assert a.avg_response_length == 0.0
        assert a.topics_discussed == []


class TestIntrospectionSnapshot:
    def test_to_dict_structure(self):
        snap = IntrospectionSnapshot()
        d = snap.to_dict()
        assert "cognitive" in d
        assert "recent_thoughts" in d
        assert "analytics" in d
        assert "timestamp" in d

    def test_to_dict_with_data(self):
        snap = IntrospectionSnapshot(
            cognitive=CognitiveSummary(mood="energized"),
            recent_thoughts=[
                ThoughtSummary(id="x", content="test", seed_type="s", created_at="now"),
            ],
        )
        d = snap.to_dict()
        assert d["cognitive"]["mood"] == "energized"
        assert len(d["recent_thoughts"]) == 1


# ── Introspector tests ───────────────────────────────────────────────────────


class TestNexusIntrospector:
    def test_snapshot_empty_log(self):
        intro = NexusIntrospector()
        snap = intro.snapshot(conversation_log=[])
        assert isinstance(snap, IntrospectionSnapshot)
        assert snap.analytics.total_messages == 0

    def test_snapshot_with_messages(self):
        intro = NexusIntrospector()
        log = [
            {"role": "user", "content": "hello", "timestamp": "10:00:00"},
            {"role": "assistant", "content": "Hi there!", "timestamp": "10:00:01"},
            {"role": "user", "content": "architecture question", "timestamp": "10:00:02"},
        ]
        snap = intro.snapshot(conversation_log=log)
        assert snap.analytics.total_messages == 3
        assert snap.analytics.user_messages == 2
        assert snap.analytics.assistant_messages == 1
        assert snap.analytics.avg_response_length > 0

    def test_record_memory_hits(self):
        intro = NexusIntrospector()
        intro.record_memory_hits(3)
        intro.record_memory_hits(2)
        snap = intro.snapshot(conversation_log=[{"role": "user", "content": "x", "timestamp": "t"}])
        assert snap.analytics.memory_hits_total == 5

    def test_reset_clears_state(self):
        intro = NexusIntrospector()
        intro.record_memory_hits(10)
        intro.reset()
        snap = intro.snapshot(conversation_log=[{"role": "user", "content": "x", "timestamp": "t"}])
        assert snap.analytics.memory_hits_total == 0

    def test_topics_deduplication(self):
        intro = NexusIntrospector()
        log = [
            {"role": "user", "content": "hello", "timestamp": "t"},
            {"role": "user", "content": "hello", "timestamp": "t"},
            {"role": "user", "content": "different topic", "timestamp": "t"},
        ]
        snap = intro.snapshot(conversation_log=log)
        assert len(snap.analytics.topics_discussed) == 2

    def test_topics_capped_at_8(self):
        intro = NexusIntrospector()
        log = [{"role": "user", "content": f"topic {i}", "timestamp": "t"} for i in range(15)]
        snap = intro.snapshot(conversation_log=log)
        assert len(snap.analytics.topics_discussed) <= 8

    def test_cognitive_read_fallback(self):
        """If cognitive read fails, snapshot still works with defaults."""
        intro = NexusIntrospector()
        # Patch the module-level import inside _read_cognitive
        with patch.dict("sys.modules", {"timmy.cognitive_state": None}):
            snap = intro.snapshot(conversation_log=[])
        # Should not raise — fallback to default
        assert snap.cognitive.mood == "settled"

    def test_thoughts_read_fallback(self):
        """If thought read fails, snapshot still works with empty list."""
        intro = NexusIntrospector()
        with patch.dict("sys.modules", {"timmy.thinking": None}):
            snap = intro.snapshot(conversation_log=[])
        assert snap.recent_thoughts == []

    def test_read_cognitive_from_tracker(self):
        intro = NexusIntrospector()
        mock_state = MagicMock()
        mock_state.mood = "curious"
        mock_state.engagement = "deep"
        mock_state.focus_topic = "sovereignty"
        mock_state.conversation_depth = 5
        mock_state.active_commitments = ["build something"]
        mock_state.last_initiative = "build something"

        mock_tracker = MagicMock()
        mock_tracker.get_state.return_value = mock_state

        with patch("timmy.cognitive_state.cognitive_tracker", mock_tracker):
            summary = intro._read_cognitive()

        assert summary.mood == "curious"
        assert summary.engagement == "deep"
        assert summary.focus_topic == "sovereignty"
        assert summary.conversation_depth == 5

    def test_read_thoughts_from_engine(self):
        intro = NexusIntrospector()
        mock_thought = MagicMock()
        mock_thought.id = "t1"
        mock_thought.content = "Deep thought about sovereignty"
        mock_thought.seed_type = "existential"
        mock_thought.created_at = "2026-03-23T10:00:00"
        mock_thought.parent_id = None

        mock_engine = MagicMock()
        mock_engine.get_recent_thoughts.return_value = [mock_thought]

        with patch("timmy.thinking.thinking_engine", mock_engine):
            thoughts = intro._read_thoughts(limit=5)

        assert len(thoughts) == 1
        assert thoughts[0].id == "t1"
        assert thoughts[0].seed_type == "existential"

    def test_read_thoughts_truncates_long_content(self):
        intro = NexusIntrospector()
        mock_thought = MagicMock()
        mock_thought.id = "t2"
        mock_thought.content = "x" * 300
        mock_thought.seed_type = "freeform"
        mock_thought.created_at = "2026-03-23"
        mock_thought.parent_id = None

        mock_engine = MagicMock()
        mock_engine.get_recent_thoughts.return_value = [mock_thought]

        with patch("timmy.thinking.thinking_engine", mock_engine):
            thoughts = intro._read_thoughts(limit=5)

        assert len(thoughts[0].content) <= 201  # 200 + "…"
