"""
Tests for Memory System Types.
"""

from datetime import UTC, datetime, timedelta
from uuid import uuid4

from app.services.memory.types import (
    ConsolidationStatus,
    ConsolidationType,
    EpisodeCreate,
    Fact,
    FactCreate,
    MemoryItem,
    MemoryStats,
    MemoryType,
    Outcome,
    Procedure,
    ProcedureCreate,
    RetrievalResult,
    ScopeContext,
    ScopeLevel,
    Step,
    TaskState,
    WorkingMemoryItem,
)


class TestEnums:
    """Tests for memory enums."""

    def test_memory_type_values(self) -> None:
        """Test MemoryType enum values."""
        assert MemoryType.WORKING == "working"
        assert MemoryType.EPISODIC == "episodic"
        assert MemoryType.SEMANTIC == "semantic"
        assert MemoryType.PROCEDURAL == "procedural"

    def test_scope_level_values(self) -> None:
        """Test ScopeLevel enum values."""
        assert ScopeLevel.GLOBAL == "global"
        assert ScopeLevel.PROJECT == "project"
        assert ScopeLevel.AGENT_TYPE == "agent_type"
        assert ScopeLevel.AGENT_INSTANCE == "agent_instance"
        assert ScopeLevel.SESSION == "session"

    def test_outcome_values(self) -> None:
        """Test Outcome enum values."""
        assert Outcome.SUCCESS == "success"
        assert Outcome.FAILURE == "failure"
        assert Outcome.PARTIAL == "partial"

    def test_consolidation_status_values(self) -> None:
        """Test ConsolidationStatus enum values."""
        assert ConsolidationStatus.PENDING == "pending"
        assert ConsolidationStatus.RUNNING == "running"
        assert ConsolidationStatus.COMPLETED == "completed"
        assert ConsolidationStatus.FAILED == "failed"

    def test_consolidation_type_values(self) -> None:
        """Test ConsolidationType enum values."""
        assert ConsolidationType.WORKING_TO_EPISODIC == "working_to_episodic"
        assert ConsolidationType.EPISODIC_TO_SEMANTIC == "episodic_to_semantic"
        assert ConsolidationType.EPISODIC_TO_PROCEDURAL == "episodic_to_procedural"
        assert ConsolidationType.PRUNING == "pruning"


class TestScopeContext:
    """Tests for ScopeContext dataclass."""

    def test_create_scope_context(self) -> None:
        """Test creating a scope context."""
        scope = ScopeContext(
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-123",
        )

        assert scope.scope_type == ScopeLevel.SESSION
        assert scope.scope_id == "sess-123"
        assert scope.parent is None

    def test_scope_with_parent(self) -> None:
        """Test creating a scope with parent."""
        parent = ScopeContext(
            scope_type=ScopeLevel.PROJECT,
            scope_id="proj-123",
        )
        child = ScopeContext(
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-456",
            parent=parent,
        )

        assert child.parent is parent
        assert child.parent.scope_type == ScopeLevel.PROJECT

    def test_get_hierarchy(self) -> None:
        """Test getting scope hierarchy."""
        global_scope = ScopeContext(
            scope_type=ScopeLevel.GLOBAL,
            scope_id="global",
        )
        project_scope = ScopeContext(
            scope_type=ScopeLevel.PROJECT,
            scope_id="proj-123",
            parent=global_scope,
        )
        session_scope = ScopeContext(
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-456",
            parent=project_scope,
        )

        hierarchy = session_scope.get_hierarchy()

        assert len(hierarchy) == 3
        assert hierarchy[0].scope_type == ScopeLevel.GLOBAL
        assert hierarchy[1].scope_type == ScopeLevel.PROJECT
        assert hierarchy[2].scope_type == ScopeLevel.SESSION

    def test_to_key_prefix(self) -> None:
        """Test converting scope to key prefix."""
        scope = ScopeContext(
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-123",
        )

        prefix = scope.to_key_prefix()
        assert prefix == "session:sess-123"


class TestMemoryItem:
    """Tests for MemoryItem dataclass."""

    def test_create_memory_item(self) -> None:
        """Test creating a memory item."""
        now = datetime.now()
        item = MemoryItem(
            id=uuid4(),
            memory_type=MemoryType.EPISODIC,
            scope_type=ScopeLevel.PROJECT,
            scope_id="proj-123",
            created_at=now,
            updated_at=now,
        )

        assert item.memory_type == MemoryType.EPISODIC
        assert item.scope_type == ScopeLevel.PROJECT
        assert item.metadata == {}

    def test_get_age_seconds(self) -> None:
        """Test getting item age."""
        past = datetime.now(UTC) - timedelta(seconds=100)
        item = MemoryItem(
            id=uuid4(),
            memory_type=MemoryType.SEMANTIC,
            scope_type=ScopeLevel.GLOBAL,
            scope_id="global",
            created_at=past,
            updated_at=past,
        )

        age = item.get_age_seconds()
        assert age >= 100
        assert age < 105  # Allow small margin


class TestWorkingMemoryItem:
    """Tests for WorkingMemoryItem dataclass."""

    def test_create_working_memory_item(self) -> None:
        """Test creating a working memory item."""
        item = WorkingMemoryItem(
            id=uuid4(),
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-123",
            key="my_key",
            value={"data": "value"},
        )

        assert item.key == "my_key"
        assert item.value == {"data": "value"}
        assert item.expires_at is None

    def test_is_expired_no_expiry(self) -> None:
        """Test is_expired with no expiry set."""
        item = WorkingMemoryItem(
            id=uuid4(),
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-123",
            key="my_key",
            value="value",
        )

        assert item.is_expired() is False

    def test_is_expired_future(self) -> None:
        """Test is_expired with future expiry."""
        item = WorkingMemoryItem(
            id=uuid4(),
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-123",
            key="my_key",
            value="value",
            expires_at=datetime.now(UTC) + timedelta(hours=1),
        )

        assert item.is_expired() is False

    def test_is_expired_past(self) -> None:
        """Test is_expired with past expiry."""
        item = WorkingMemoryItem(
            id=uuid4(),
            scope_type=ScopeLevel.SESSION,
            scope_id="sess-123",
            key="my_key",
            value="value",
            expires_at=datetime.now(UTC) - timedelta(hours=1),
        )

        assert item.is_expired() is True


class TestTaskState:
    """Tests for TaskState dataclass."""

    def test_create_task_state(self) -> None:
        """Test creating a task state."""
        state = TaskState(
            task_id="task-123",
            task_type="code_review",
            description="Review PR #42",
        )

        assert state.task_id == "task-123"
        assert state.task_type == "code_review"
        assert state.status == "in_progress"
        assert state.current_step == 0
        assert state.progress_percent == 0.0

    def test_task_state_with_progress(self) -> None:
        """Test task state with progress."""
        state = TaskState(
            task_id="task-123",
            task_type="implementation",
            description="Implement feature X",
            current_step=3,
            total_steps=5,
            progress_percent=60.0,
        )

        assert state.current_step == 3
        assert state.total_steps == 5
        assert state.progress_percent == 60.0


class TestEpisode:
    """Tests for Episode and EpisodeCreate dataclasses."""

    def test_create_episode_data(self) -> None:
        """Test creating episode create data."""
        data = EpisodeCreate(
            project_id=uuid4(),
            session_id="sess-123",
            task_type="bug_fix",
            task_description="Fix login bug",
            actions=[{"action": "read_file", "file": "auth.py"}],
            context_summary="User reported login issues",
            outcome=Outcome.SUCCESS,
            outcome_details="Fixed by updating validation",
            duration_seconds=120.5,
            tokens_used=5000,
        )

        assert data.task_type == "bug_fix"
        assert data.outcome == Outcome.SUCCESS
        assert len(data.actions) == 1
        assert data.importance_score == 0.5  # Default


class TestFact:
    """Tests for Fact and FactCreate dataclasses."""

    def test_create_fact_data(self) -> None:
        """Test creating fact create data."""
        data = FactCreate(
            subject="FastAPI",
            predicate="uses",
            object="Starlette framework",
        )

        assert data.subject == "FastAPI"
        assert data.predicate == "uses"
        assert data.object == "Starlette framework"
        assert data.confidence == 0.8  # Default
        assert data.project_id is None  # Global fact


class TestProcedure:
    """Tests for Procedure and ProcedureCreate dataclasses."""

    def test_create_procedure_data(self) -> None:
        """Test creating procedure create data."""
        data = ProcedureCreate(
            name="review_pr",
            trigger_pattern="review pull request",
            steps=[
                {"action": "checkout_branch"},
                {"action": "run_tests"},
                {"action": "review_changes"},
            ],
        )

        assert data.name == "review_pr"
        assert len(data.steps) == 3

    def test_procedure_success_rate(self) -> None:
        """Test procedure success rate calculation."""
        now = datetime.now()
        procedure = Procedure(
            id=uuid4(),
            project_id=None,
            agent_type_id=None,
            name="test_proc",
            trigger_pattern="test",
            steps=[],
            success_count=8,
            failure_count=2,
            last_used=now,
            embedding=None,
            created_at=now,
            updated_at=now,
        )

        assert procedure.success_rate == 0.8

    def test_procedure_success_rate_zero_uses(self) -> None:
        """Test procedure success rate with zero uses."""
        now = datetime.now()
        procedure = Procedure(
            id=uuid4(),
            project_id=None,
            agent_type_id=None,
            name="test_proc",
            trigger_pattern="test",
            steps=[],
            success_count=0,
            failure_count=0,
            last_used=None,
            embedding=None,
            created_at=now,
            updated_at=now,
        )

        assert procedure.success_rate == 0.0


class TestStep:
    """Tests for Step dataclass."""

    def test_create_step(self) -> None:
        """Test creating a step."""
        step = Step(
            order=1,
            action="run_tests",
            parameters={"verbose": True},
            expected_outcome="All tests pass",
        )

        assert step.order == 1
        assert step.action == "run_tests"
        assert step.parameters == {"verbose": True}


class TestRetrievalResult:
    """Tests for RetrievalResult dataclass."""

    def test_create_retrieval_result(self) -> None:
        """Test creating a retrieval result."""
        result: RetrievalResult[Fact] = RetrievalResult(
            items=[],
            total_count=0,
            query="test query",
            retrieval_type="semantic",
            latency_ms=15.5,
        )

        assert result.query == "test query"
        assert result.latency_ms == 15.5
        assert result.metadata == {}


class TestMemoryStats:
    """Tests for MemoryStats dataclass."""

    def test_create_memory_stats(self) -> None:
        """Test creating memory stats."""
        stats = MemoryStats(
            memory_type=MemoryType.EPISODIC,
            scope_type=ScopeLevel.PROJECT,
            scope_id="proj-123",
            item_count=150,
            total_size_bytes=1048576,
            oldest_item_age_seconds=86400,
            newest_item_age_seconds=60,
            avg_item_size_bytes=6990.5,
        )

        assert stats.memory_type == MemoryType.EPISODIC
        assert stats.item_count == 150
        assert stats.total_size_bytes == 1048576
