"""Nexus Introspection Engine — cognitive self-awareness for Timmy.

Aggregates live signals from the CognitiveTracker, ThinkingEngine, and
MemorySystem into a unified introspection snapshot. The Nexus template
renders this as an always-visible cognitive state panel so the operator
can observe Timmy's inner life in real time.

Design principles:
- Read-only observer — never mutates cognitive state.
- Graceful degradation — if any upstream is unavailable, the snapshot
  still returns with partial data instead of crashing.
- JSON-serializable — every method returns plain dicts ready for
  WebSocket push or Jinja2 template rendering.

Refs: #1090 (Nexus Epic), architecture-v2.md §Intelligence Surface
"""

from __future__ import annotations

import logging
from dataclasses import asdict, dataclass, field
from datetime import UTC, datetime

logger = logging.getLogger(__name__)


# ── Data models ──────────────────────────────────────────────────────────────


@dataclass
class CognitiveSummary:
    """Distilled view of Timmy's current cognitive state."""

    mood: str = "settled"
    engagement: str = "idle"
    focus_topic: str | None = None
    conversation_depth: int = 0
    active_commitments: list[str] = field(default_factory=list)
    last_initiative: str | None = None

    def to_dict(self) -> dict:
        return asdict(self)


@dataclass
class ThoughtSummary:
    """Compact representation of a single thought for the Nexus viewer."""

    id: str
    content: str
    seed_type: str
    created_at: str
    parent_id: str | None = None

    def to_dict(self) -> dict:
        return asdict(self)


@dataclass
class SessionAnalytics:
    """Conversation-level analytics for the active Nexus session."""

    total_messages: int = 0
    user_messages: int = 0
    assistant_messages: int = 0
    avg_response_length: float = 0.0
    topics_discussed: list[str] = field(default_factory=list)
    session_start: str | None = None
    session_duration_minutes: float = 0.0
    memory_hits_total: int = 0

    def to_dict(self) -> dict:
        return asdict(self)


@dataclass
class IntrospectionSnapshot:
    """Everything the Nexus template needs to render the cognitive panel."""

    cognitive: CognitiveSummary = field(default_factory=CognitiveSummary)
    recent_thoughts: list[ThoughtSummary] = field(default_factory=list)
    analytics: SessionAnalytics = field(default_factory=SessionAnalytics)
    timestamp: str = field(default_factory=lambda: datetime.now(UTC).isoformat())

    def to_dict(self) -> dict:
        return {
            "cognitive": self.cognitive.to_dict(),
            "recent_thoughts": [t.to_dict() for t in self.recent_thoughts],
            "analytics": self.analytics.to_dict(),
            "timestamp": self.timestamp,
        }


# ── Introspector ─────────────────────────────────────────────────────────────


class NexusIntrospector:
    """Aggregates cognitive signals into a single introspection snapshot.

    Lazily pulls from:
    - ``timmy.cognitive_state.cognitive_tracker``
    - ``timmy.thinking.thinking_engine``
    - Nexus conversation log (passed in to avoid circular import)
    """

    def __init__(self) -> None:
        self._session_start: datetime | None = None
        self._topics: list[str] = []
        self._memory_hit_count: int = 0

    # ── Public API ────────────────────────────────────────────────────────

    def snapshot(
        self,
        conversation_log: list[dict] | None = None,
    ) -> IntrospectionSnapshot:
        """Build a complete introspection snapshot.

        Parameters
        ----------
        conversation_log:
            The in-memory ``_nexus_log`` from the routes module (list of
            dicts with ``role``, ``content``, ``timestamp`` keys).
        """
        return IntrospectionSnapshot(
            cognitive=self._read_cognitive(),
            recent_thoughts=self._read_thoughts(),
            analytics=self._compute_analytics(conversation_log or []),
        )

    def record_memory_hits(self, count: int) -> None:
        """Track cumulative memory hits for session analytics."""
        self._memory_hit_count += count

    def reset(self) -> None:
        """Reset session-scoped analytics (e.g. on history clear)."""
        self._session_start = None
        self._topics.clear()
        self._memory_hit_count = 0

    # ── Cognitive state reader ────────────────────────────────────────────

    def _read_cognitive(self) -> CognitiveSummary:
        """Pull current state from the CognitiveTracker singleton."""
        try:
            from timmy.cognitive_state import cognitive_tracker

            state = cognitive_tracker.get_state()
            return CognitiveSummary(
                mood=state.mood,
                engagement=state.engagement,
                focus_topic=state.focus_topic,
                conversation_depth=state.conversation_depth,
                active_commitments=list(state.active_commitments),
                last_initiative=state.last_initiative,
            )
        except Exception as exc:
            logger.debug("Introspection: cognitive state unavailable: %s", exc)
            return CognitiveSummary()

    # ── Thought stream reader ─────────────────────────────────────────────

    def _read_thoughts(self, limit: int = 5) -> list[ThoughtSummary]:
        """Pull recent thoughts from the ThinkingEngine."""
        try:
            from timmy.thinking import thinking_engine

            thoughts = thinking_engine.get_recent_thoughts(limit=limit)
            return [
                ThoughtSummary(
                    id=t.id,
                    content=(t.content[:200] + "…" if len(t.content) > 200 else t.content),
                    seed_type=t.seed_type,
                    created_at=t.created_at,
                    parent_id=t.parent_id,
                )
                for t in thoughts
            ]
        except Exception as exc:
            logger.debug("Introspection: thought stream unavailable: %s", exc)
            return []

    # ── Session analytics ─────────────────────────────────────────────────

    def _compute_analytics(self, conversation_log: list[dict]) -> SessionAnalytics:
        """Derive analytics from the Nexus conversation log."""
        if not conversation_log:
            return SessionAnalytics()

        if self._session_start is None:
            self._session_start = datetime.now(UTC)

        user_msgs = [m for m in conversation_log if m.get("role") == "user"]
        asst_msgs = [m for m in conversation_log if m.get("role") == "assistant"]

        avg_len = 0.0
        if asst_msgs:
            total_chars = sum(len(m.get("content", "")) for m in asst_msgs)
            avg_len = total_chars / len(asst_msgs)

        # Extract topics from user messages (simple: first 40 chars)
        topics = []
        seen: set[str] = set()
        for m in user_msgs:
            topic = m.get("content", "")[:40].strip()
            if topic and topic.lower() not in seen:
                topics.append(topic)
                seen.add(topic.lower())
        # Keep last 8 topics
        topics = topics[-8:]

        elapsed = (datetime.now(UTC) - self._session_start).total_seconds() / 60

        return SessionAnalytics(
            total_messages=len(conversation_log),
            user_messages=len(user_msgs),
            assistant_messages=len(asst_msgs),
            avg_response_length=round(avg_len, 1),
            topics_discussed=topics,
            session_start=self._session_start.strftime("%H:%M:%S"),
            session_duration_minutes=round(elapsed, 1),
            memory_hits_total=self._memory_hit_count,
        )


# ── Module singleton ─────────────────────────────────────────────────────────

nexus_introspector = NexusIntrospector()
