# tests/unit/services/context/types/test_memory.py
"""Tests for MemoryContext type."""

from datetime import UTC, datetime
from unittest.mock import MagicMock
from uuid import uuid4

from app.services.context.types import ContextType
from app.services.context.types.memory import MemoryContext, MemorySubtype


class TestMemorySubtype:
    """Tests for MemorySubtype enum."""

    def test_all_types_defined(self) -> None:
        """All memory subtypes should be defined."""
        assert MemorySubtype.WORKING == "working"
        assert MemorySubtype.EPISODIC == "episodic"
        assert MemorySubtype.SEMANTIC == "semantic"
        assert MemorySubtype.PROCEDURAL == "procedural"

    def test_enum_values(self) -> None:
        """Enum values should match strings."""
        assert MemorySubtype.WORKING.value == "working"
        assert MemorySubtype("episodic") == MemorySubtype.EPISODIC


class TestMemoryContext:
    """Tests for MemoryContext class."""

    def test_get_type_returns_memory(self) -> None:
        """get_type should return MEMORY."""
        ctx = MemoryContext(content="test", source="test_source")
        assert ctx.get_type() == ContextType.MEMORY

    def test_default_values(self) -> None:
        """Default values should be set correctly."""
        ctx = MemoryContext(content="test", source="test_source")
        assert ctx.memory_subtype == MemorySubtype.EPISODIC
        assert ctx.memory_id is None
        assert ctx.relevance_score == 0.0
        assert ctx.importance == 0.5

    def test_to_dict_includes_memory_fields(self) -> None:
        """to_dict should include memory-specific fields."""
        ctx = MemoryContext(
            content="test content",
            source="test_source",
            memory_subtype=MemorySubtype.SEMANTIC,
            memory_id="mem-123",
            relevance_score=0.8,
            subject="User",
            predicate="prefers",
            object_value="dark mode",
        )

        data = ctx.to_dict()

        assert data["memory_subtype"] == "semantic"
        assert data["memory_id"] == "mem-123"
        assert data["relevance_score"] == 0.8
        assert data["subject"] == "User"
        assert data["predicate"] == "prefers"
        assert data["object_value"] == "dark mode"

    def test_from_dict(self) -> None:
        """from_dict should create correct MemoryContext."""
        data = {
            "content": "test content",
            "source": "test_source",
            "timestamp": "2024-01-01T00:00:00+00:00",
            "memory_subtype": "semantic",
            "memory_id": "mem-123",
            "relevance_score": 0.8,
            "subject": "Test",
        }

        ctx = MemoryContext.from_dict(data)

        assert ctx.content == "test content"
        assert ctx.memory_subtype == MemorySubtype.SEMANTIC
        assert ctx.memory_id == "mem-123"
        assert ctx.subject == "Test"


class TestMemoryContextFromWorkingMemory:
    """Tests for MemoryContext.from_working_memory."""

    def test_creates_working_memory_context(self) -> None:
        """Should create working memory context from key/value."""
        ctx = MemoryContext.from_working_memory(
            key="user_preferences",
            value={"theme": "dark"},
            source="working:sess-123",
            query="preferences",
        )

        assert ctx.memory_subtype == MemorySubtype.WORKING
        assert ctx.key == "user_preferences"
        assert "{'theme': 'dark'}" in ctx.content
        assert ctx.relevance_score == 1.0  # Working memory is always relevant
        assert ctx.importance == 0.8  # Higher importance

    def test_string_value(self) -> None:
        """Should handle string values."""
        ctx = MemoryContext.from_working_memory(
            key="current_task",
            value="Build authentication",
        )

        assert ctx.content == "Build authentication"


class TestMemoryContextFromEpisodicMemory:
    """Tests for MemoryContext.from_episodic_memory."""

    def test_creates_episodic_memory_context(self) -> None:
        """Should create episodic memory context from episode."""
        episode = MagicMock()
        episode.id = uuid4()
        episode.task_description = "Implemented login feature"
        episode.task_type = "feature_implementation"
        episode.outcome = MagicMock(value="success")
        episode.importance_score = 0.9
        episode.session_id = "sess-123"
        episode.occurred_at = datetime.now(UTC)
        episode.lessons_learned = ["Use proper validation"]

        ctx = MemoryContext.from_episodic_memory(episode, query="login")

        assert ctx.memory_subtype == MemorySubtype.EPISODIC
        assert ctx.memory_id == str(episode.id)
        assert ctx.content == "Implemented login feature"
        assert ctx.task_type == "feature_implementation"
        assert ctx.outcome == "success"
        assert ctx.importance == 0.9

    def test_handles_missing_outcome(self) -> None:
        """Should handle episodes with no outcome."""
        episode = MagicMock()
        episode.id = uuid4()
        episode.task_description = "WIP task"
        episode.outcome = None
        episode.importance_score = 0.5
        episode.occurred_at = None

        ctx = MemoryContext.from_episodic_memory(episode)

        assert ctx.outcome is None


class TestMemoryContextFromSemanticMemory:
    """Tests for MemoryContext.from_semantic_memory."""

    def test_creates_semantic_memory_context(self) -> None:
        """Should create semantic memory context from fact."""
        fact = MagicMock()
        fact.id = uuid4()
        fact.subject = "User"
        fact.predicate = "prefers"
        fact.object = "dark mode"
        fact.confidence = 0.95

        ctx = MemoryContext.from_semantic_memory(fact, query="user preferences")

        assert ctx.memory_subtype == MemorySubtype.SEMANTIC
        assert ctx.memory_id == str(fact.id)
        assert ctx.content == "User prefers dark mode"
        assert ctx.subject == "User"
        assert ctx.predicate == "prefers"
        assert ctx.object_value == "dark mode"
        assert ctx.relevance_score == 0.95


class TestMemoryContextFromProceduralMemory:
    """Tests for MemoryContext.from_procedural_memory."""

    def test_creates_procedural_memory_context(self) -> None:
        """Should create procedural memory context from procedure."""
        procedure = MagicMock()
        procedure.id = uuid4()
        procedure.name = "Deploy to Production"
        procedure.trigger_pattern = "When deploying to production"
        procedure.steps = [
            {"action": "run_tests"},
            {"action": "build_docker"},
            {"action": "deploy"},
        ]
        procedure.success_rate = 0.85
        procedure.success_count = 10
        procedure.failure_count = 2

        ctx = MemoryContext.from_procedural_memory(procedure, query="deploy")

        assert ctx.memory_subtype == MemorySubtype.PROCEDURAL
        assert ctx.memory_id == str(procedure.id)
        assert "Deploy to Production" in ctx.content
        assert "When deploying to production" in ctx.content
        assert ctx.trigger == "When deploying to production"
        assert ctx.success_rate == 0.85
        assert ctx.metadata["steps_count"] == 3
        assert ctx.metadata["execution_count"] == 12


class TestMemoryContextHelpers:
    """Tests for MemoryContext helper methods."""

    def test_is_working_memory(self) -> None:
        """is_working_memory should return True for working memory."""
        ctx = MemoryContext(
            content="test",
            source="test",
            memory_subtype=MemorySubtype.WORKING,
        )
        assert ctx.is_working_memory() is True
        assert ctx.is_episodic_memory() is False

    def test_is_episodic_memory(self) -> None:
        """is_episodic_memory should return True for episodic memory."""
        ctx = MemoryContext(
            content="test",
            source="test",
            memory_subtype=MemorySubtype.EPISODIC,
        )
        assert ctx.is_episodic_memory() is True
        assert ctx.is_semantic_memory() is False

    def test_is_semantic_memory(self) -> None:
        """is_semantic_memory should return True for semantic memory."""
        ctx = MemoryContext(
            content="test",
            source="test",
            memory_subtype=MemorySubtype.SEMANTIC,
        )
        assert ctx.is_semantic_memory() is True
        assert ctx.is_procedural_memory() is False

    def test_is_procedural_memory(self) -> None:
        """is_procedural_memory should return True for procedural memory."""
        ctx = MemoryContext(
            content="test",
            source="test",
            memory_subtype=MemorySubtype.PROCEDURAL,
        )
        assert ctx.is_procedural_memory() is True
        assert ctx.is_working_memory() is False

    def test_get_formatted_source(self) -> None:
        """get_formatted_source should return formatted string."""
        ctx = MemoryContext(
            content="test",
            source="episodic:12345678-1234-1234-1234-123456789012",
            memory_subtype=MemorySubtype.EPISODIC,
            memory_id="12345678-1234-1234-1234-123456789012",
        )

        formatted = ctx.get_formatted_source()

        assert "[episodic]" in formatted
        assert "12345678..." in formatted
