"""
Memory Context Type.

Represents agent memory as context for LLM requests.
Includes working, episodic, semantic, and procedural memories.
"""

from dataclasses import dataclass, field
from datetime import UTC, datetime
from enum import Enum
from typing import Any

from .base import BaseContext, ContextPriority, ContextType


class MemorySubtype(str, Enum):
    """Types of agent memory."""

    WORKING = "working"  # Session-scoped temporary data
    EPISODIC = "episodic"  # Task history and outcomes
    SEMANTIC = "semantic"  # Facts and knowledge
    PROCEDURAL = "procedural"  # Learned procedures


@dataclass(eq=False)
class MemoryContext(BaseContext):
    """
    Context from agent memory system.

    Memory context represents data retrieved from the agent
    memory system, including:
    - Working memory: Current session state
    - Episodic memory: Past task experiences
    - Semantic memory: Learned facts and knowledge
    - Procedural memory: Known procedures and workflows

    Each memory item includes relevance scoring from search.
    """

    # Memory-specific fields
    memory_subtype: MemorySubtype = field(default=MemorySubtype.EPISODIC)
    memory_id: str | None = field(default=None)
    relevance_score: float = field(default=0.0)
    importance: float = field(default=0.5)
    search_query: str = field(default="")

    # Type-specific fields (populated based on memory_subtype)
    key: str | None = field(default=None)  # For working memory
    task_type: str | None = field(default=None)  # For episodic
    outcome: str | None = field(default=None)  # For episodic
    subject: str | None = field(default=None)  # For semantic
    predicate: str | None = field(default=None)  # For semantic
    object_value: str | None = field(default=None)  # For semantic
    trigger: str | None = field(default=None)  # For procedural
    success_rate: float | None = field(default=None)  # For procedural

    def get_type(self) -> ContextType:
        """Return MEMORY context type."""
        return ContextType.MEMORY

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary with memory-specific fields."""
        base = super().to_dict()
        base.update(
            {
                "memory_subtype": self.memory_subtype.value,
                "memory_id": self.memory_id,
                "relevance_score": self.relevance_score,
                "importance": self.importance,
                "search_query": self.search_query,
                "key": self.key,
                "task_type": self.task_type,
                "outcome": self.outcome,
                "subject": self.subject,
                "predicate": self.predicate,
                "object_value": self.object_value,
                "trigger": self.trigger,
                "success_rate": self.success_rate,
            }
        )
        return base

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "MemoryContext":
        """Create MemoryContext from dictionary."""
        return cls(
            id=data.get("id", ""),
            content=data["content"],
            source=data["source"],
            timestamp=datetime.fromisoformat(data["timestamp"])
            if isinstance(data.get("timestamp"), str)
            else data.get("timestamp", datetime.now(UTC)),
            priority=data.get("priority", ContextPriority.NORMAL.value),
            metadata=data.get("metadata", {}),
            memory_subtype=MemorySubtype(data.get("memory_subtype", "episodic")),
            memory_id=data.get("memory_id"),
            relevance_score=data.get("relevance_score", 0.0),
            importance=data.get("importance", 0.5),
            search_query=data.get("search_query", ""),
            key=data.get("key"),
            task_type=data.get("task_type"),
            outcome=data.get("outcome"),
            subject=data.get("subject"),
            predicate=data.get("predicate"),
            object_value=data.get("object_value"),
            trigger=data.get("trigger"),
            success_rate=data.get("success_rate"),
        )

    @classmethod
    def from_working_memory(
        cls,
        key: str,
        value: Any,
        source: str = "working_memory",
        query: str = "",
    ) -> "MemoryContext":
        """
        Create MemoryContext from working memory entry.

        Args:
            key: Working memory key
            value: Value stored at key
            source: Source identifier
            query: Search query used

        Returns:
            MemoryContext instance
        """
        return cls(
            content=str(value),
            source=source,
            memory_subtype=MemorySubtype.WORKING,
            key=key,
            relevance_score=1.0,  # Working memory is always relevant
            importance=0.8,  # Higher importance for current session state
            search_query=query,
            priority=ContextPriority.HIGH.value,
        )

    @classmethod
    def from_episodic_memory(
        cls,
        episode: Any,
        query: str = "",
    ) -> "MemoryContext":
        """
        Create MemoryContext from episodic memory episode.

        Args:
            episode: Episode object from episodic memory
            query: Search query used

        Returns:
            MemoryContext instance
        """
        outcome_val = None
        if hasattr(episode, "outcome") and episode.outcome:
            outcome_val = (
                episode.outcome.value
                if hasattr(episode.outcome, "value")
                else str(episode.outcome)
            )

        return cls(
            content=episode.task_description,
            source=f"episodic:{episode.id}",
            memory_subtype=MemorySubtype.EPISODIC,
            memory_id=str(episode.id),
            relevance_score=getattr(episode, "importance_score", 0.5),
            importance=getattr(episode, "importance_score", 0.5),
            search_query=query,
            task_type=getattr(episode, "task_type", None),
            outcome=outcome_val,
            metadata={
                "session_id": getattr(episode, "session_id", None),
                "occurred_at": episode.occurred_at.isoformat()
                if hasattr(episode, "occurred_at") and episode.occurred_at
                else None,
                "lessons_learned": getattr(episode, "lessons_learned", []),
            },
        )

    @classmethod
    def from_semantic_memory(
        cls,
        fact: Any,
        query: str = "",
    ) -> "MemoryContext":
        """
        Create MemoryContext from semantic memory fact.

        Args:
            fact: Fact object from semantic memory
            query: Search query used

        Returns:
            MemoryContext instance
        """
        triple = f"{fact.subject} {fact.predicate} {fact.object}"
        return cls(
            content=triple,
            source=f"semantic:{fact.id}",
            memory_subtype=MemorySubtype.SEMANTIC,
            memory_id=str(fact.id),
            relevance_score=getattr(fact, "confidence", 0.5),
            importance=getattr(fact, "confidence", 0.5),
            search_query=query,
            subject=fact.subject,
            predicate=fact.predicate,
            object_value=fact.object,
            priority=ContextPriority.NORMAL.value,
        )

    @classmethod
    def from_procedural_memory(
        cls,
        procedure: Any,
        query: str = "",
    ) -> "MemoryContext":
        """
        Create MemoryContext from procedural memory procedure.

        Args:
            procedure: Procedure object from procedural memory
            query: Search query used

        Returns:
            MemoryContext instance
        """
        # Format steps as content
        steps = getattr(procedure, "steps", [])
        steps_content = "\n".join(
            f"  {i + 1}. {step.get('action', step) if isinstance(step, dict) else step}"
            for i, step in enumerate(steps)
        )
        content = f"Procedure: {procedure.name}\nTrigger: {procedure.trigger_pattern}\nSteps:\n{steps_content}"

        return cls(
            content=content,
            source=f"procedural:{procedure.id}",
            memory_subtype=MemorySubtype.PROCEDURAL,
            memory_id=str(procedure.id),
            relevance_score=getattr(procedure, "success_rate", 0.5),
            importance=0.7,  # Procedures are moderately important
            search_query=query,
            trigger=procedure.trigger_pattern,
            success_rate=getattr(procedure, "success_rate", None),
            metadata={
                "steps_count": len(steps),
                "execution_count": getattr(procedure, "success_count", 0)
                + getattr(procedure, "failure_count", 0),
            },
        )

    def is_working_memory(self) -> bool:
        """Check if this is working memory."""
        return self.memory_subtype == MemorySubtype.WORKING

    def is_episodic_memory(self) -> bool:
        """Check if this is episodic memory."""
        return self.memory_subtype == MemorySubtype.EPISODIC

    def is_semantic_memory(self) -> bool:
        """Check if this is semantic memory."""
        return self.memory_subtype == MemorySubtype.SEMANTIC

    def is_procedural_memory(self) -> bool:
        """Check if this is procedural memory."""
        return self.memory_subtype == MemorySubtype.PROCEDURAL

    def get_formatted_source(self) -> str:
        """
        Get a formatted source string for display.

        Returns:
            Formatted source string
        """
        parts = [f"[{self.memory_subtype.value}]", self.source]
        if self.memory_id:
            parts.append(f"({self.memory_id[:8]}...)")
        return " ".join(parts)
