# tests/unit/models/memory/test_models.py
"""Unit tests for memory database models."""

from datetime import UTC, datetime, timedelta

import pytest

from app.models.memory import (
    ConsolidationStatus,
    ConsolidationType,
    Episode,
    EpisodeOutcome,
    Fact,
    MemoryConsolidationLog,
    Procedure,
    ScopeType,
    WorkingMemory,
)


class TestWorkingMemoryModel:
    """Tests for WorkingMemory model."""

    def test_tablename(self) -> None:
        """Test table name is correct."""
        assert WorkingMemory.__tablename__ == "working_memory"

    def test_has_required_columns(self) -> None:
        """Test all required columns exist."""
        columns = WorkingMemory.__table__.columns
        assert "id" in columns
        assert "scope_type" in columns
        assert "scope_id" in columns
        assert "key" in columns
        assert "value" in columns
        assert "expires_at" in columns
        assert "created_at" in columns
        assert "updated_at" in columns

    def test_has_unique_constraint(self) -> None:
        """Test unique constraint on scope+key."""
        indexes = {idx.name: idx for idx in WorkingMemory.__table__.indexes}
        assert "ix_working_memory_scope_key" in indexes
        assert indexes["ix_working_memory_scope_key"].unique


class TestEpisodeModel:
    """Tests for Episode model."""

    def test_tablename(self) -> None:
        """Test table name is correct."""
        assert Episode.__tablename__ == "episodes"

    def test_has_required_columns(self) -> None:
        """Test all required columns exist."""
        columns = Episode.__table__.columns
        required = [
            "id",
            "project_id",
            "agent_instance_id",
            "agent_type_id",
            "session_id",
            "task_type",
            "task_description",
            "actions",
            "context_summary",
            "outcome",
            "outcome_details",
            "duration_seconds",
            "tokens_used",
            "lessons_learned",
            "importance_score",
            "embedding",
            "occurred_at",
            "created_at",
            "updated_at",
        ]
        for col in required:
            assert col in columns, f"Missing column: {col}"

    def test_has_foreign_keys(self) -> None:
        """Test foreign key relationships exist."""
        columns = Episode.__table__.columns
        assert columns["project_id"].foreign_keys
        assert columns["agent_instance_id"].foreign_keys
        assert columns["agent_type_id"].foreign_keys

    def test_has_relationships(self) -> None:
        """Test ORM relationships exist."""
        mapper = Episode.__mapper__
        assert "project" in mapper.relationships
        assert "agent_instance" in mapper.relationships
        assert "agent_type" in mapper.relationships


class TestFactModel:
    """Tests for Fact model."""

    def test_tablename(self) -> None:
        """Test table name is correct."""
        assert Fact.__tablename__ == "facts"

    def test_has_required_columns(self) -> None:
        """Test all required columns exist."""
        columns = Fact.__table__.columns
        required = [
            "id",
            "project_id",
            "subject",
            "predicate",
            "object",
            "confidence",
            "source_episode_ids",
            "first_learned",
            "last_reinforced",
            "reinforcement_count",
            "embedding",
            "created_at",
            "updated_at",
        ]
        for col in required:
            assert col in columns, f"Missing column: {col}"

    def test_project_id_nullable(self) -> None:
        """Test project_id is nullable for global facts."""
        columns = Fact.__table__.columns
        assert columns["project_id"].nullable


class TestProcedureModel:
    """Tests for Procedure model."""

    def test_tablename(self) -> None:
        """Test table name is correct."""
        assert Procedure.__tablename__ == "procedures"

    def test_has_required_columns(self) -> None:
        """Test all required columns exist."""
        columns = Procedure.__table__.columns
        required = [
            "id",
            "project_id",
            "agent_type_id",
            "name",
            "trigger_pattern",
            "steps",
            "success_count",
            "failure_count",
            "last_used",
            "embedding",
            "created_at",
            "updated_at",
        ]
        for col in required:
            assert col in columns, f"Missing column: {col}"

    def test_success_rate_property(self) -> None:
        """Test success_rate calculated property."""
        proc = Procedure()
        proc.success_count = 8
        proc.failure_count = 2
        assert proc.success_rate == 0.8

    def test_success_rate_zero_total(self) -> None:
        """Test success_rate with zero total uses."""
        proc = Procedure()
        proc.success_count = 0
        proc.failure_count = 0
        assert proc.success_rate == 0.0

    def test_total_uses_property(self) -> None:
        """Test total_uses calculated property."""
        proc = Procedure()
        proc.success_count = 5
        proc.failure_count = 3
        assert proc.total_uses == 8


class TestMemoryConsolidationLogModel:
    """Tests for MemoryConsolidationLog model."""

    def test_tablename(self) -> None:
        """Test table name is correct."""
        assert MemoryConsolidationLog.__tablename__ == "memory_consolidation_log"

    def test_has_required_columns(self) -> None:
        """Test all required columns exist."""
        columns = MemoryConsolidationLog.__table__.columns
        required = [
            "id",
            "consolidation_type",
            "source_count",
            "result_count",
            "started_at",
            "completed_at",
            "status",
            "error",
            "created_at",
            "updated_at",
        ]
        for col in required:
            assert col in columns, f"Missing column: {col}"

    def test_duration_seconds_property_completed(self) -> None:
        """Test duration_seconds with completed job."""
        log = MemoryConsolidationLog()
        log.started_at = datetime.now(UTC)
        log.completed_at = log.started_at + timedelta(seconds=10)
        assert log.duration_seconds == pytest.approx(10.0)

    def test_duration_seconds_property_incomplete(self) -> None:
        """Test duration_seconds with incomplete job."""
        log = MemoryConsolidationLog()
        log.started_at = datetime.now(UTC)
        log.completed_at = None
        assert log.duration_seconds is None

    def test_default_status(self) -> None:
        """Test default status is PENDING."""
        columns = MemoryConsolidationLog.__table__.columns
        assert columns["status"].default.arg == ConsolidationStatus.PENDING


class TestModelExports:
    """Tests for model package exports."""

    def test_all_models_exported(self) -> None:
        """Test all models are exported from package."""
        from app.models.memory import (
            Episode,
            Fact,
            MemoryConsolidationLog,
            Procedure,
            WorkingMemory,
        )

        # Verify these are the actual classes
        assert Episode.__tablename__ == "episodes"
        assert Fact.__tablename__ == "facts"
        assert Procedure.__tablename__ == "procedures"
        assert WorkingMemory.__tablename__ == "working_memory"
        assert MemoryConsolidationLog.__tablename__ == "memory_consolidation_log"

    def test_enums_exported(self) -> None:
        """Test all enums are exported."""
        assert ScopeType.GLOBAL.value == "global"
        assert EpisodeOutcome.SUCCESS.value == "success"
        assert ConsolidationType.WORKING_TO_EPISODIC.value == "working_to_episodic"
        assert ConsolidationStatus.PENDING.value == "pending"
