"""
Tests for BaseRepository

100% coverage tests for base repository utilities.
"""

import pytest
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch
from uuid import uuid4, UUID

from backend.data.repositories.base import BaseRepository


class ConcreteRepository(BaseRepository[MagicMock]):
    """Concrete implementation for testing abstract base class."""
    pass


class TestBaseRepository:
    """Tests for BaseRepository."""

    @pytest.fixture
    def repo(self) -> ConcreteRepository:
        """Create a ConcreteRepository instance."""
        return ConcreteRepository()

    # =========================================================================
    # _session() tests
    # =========================================================================

    def test_session_yields_session(self, repo):
        """Test _session yields a database session."""
        with patch("backend.data.repositories.base.get_session_context") as mock_ctx:
            mock_session = MagicMock()
            mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_session)
            mock_ctx.return_value.__exit__ = MagicMock(return_value=False)

            with repo._session() as session:
                assert session is mock_session

    # =========================================================================
    # _expunge() tests
    # =========================================================================

    def test_expunge_detaches_entity(self, repo):
        """Test _expunge detaches entity from session."""
        mock_session = MagicMock()
        mock_entity = MagicMock()

        result = repo._expunge(mock_session, mock_entity)

        mock_session.expunge.assert_called_once_with(mock_entity)
        assert result is mock_entity

    # =========================================================================
    # _expunge_all() tests
    # =========================================================================

    def test_expunge_all_detaches_all_entities(self, repo):
        """Test _expunge_all detaches all entities from session."""
        mock_session = MagicMock()
        mock_entity1 = MagicMock()
        mock_entity2 = MagicMock()
        entities = [mock_entity1, mock_entity2]

        result = repo._expunge_all(mock_session, entities)

        assert mock_session.expunge.call_count == 2
        mock_session.expunge.assert_any_call(mock_entity1)
        mock_session.expunge.assert_any_call(mock_entity2)
        assert result is entities

    def test_expunge_all_empty_list(self, repo):
        """Test _expunge_all with empty list."""
        mock_session = MagicMock()
        entities = []

        result = repo._expunge_all(mock_session, entities)

        mock_session.expunge.assert_not_called()
        assert result == []

    # =========================================================================
    # _now() tests
    # =========================================================================

    def test_now_returns_utc_datetime(self, repo):
        """Test _now returns timezone-aware UTC datetime."""
        result = repo._now()

        assert result.tzinfo == timezone.utc
        assert isinstance(result, datetime)

    def test_now_is_recent(self, repo):
        """Test _now returns a recent datetime."""
        before = datetime.now(timezone.utc)
        result = repo._now()
        after = datetime.now(timezone.utc)

        assert before <= result <= after

    # =========================================================================
    # _validate_uuid() tests
    # =========================================================================

    def test_validate_uuid_with_valid_string(self, repo):
        """Test _validate_uuid with valid UUID string."""
        valid_uuid_str = str(uuid4())

        result = repo._validate_uuid(valid_uuid_str)

        assert isinstance(result, UUID)
        assert str(result) == valid_uuid_str

    def test_validate_uuid_with_invalid_string(self, repo):
        """Test _validate_uuid raises ValueError for invalid UUID."""
        with pytest.raises(ValueError) as exc_info:
            repo._validate_uuid("not-a-valid-uuid")

        assert "Invalid id" in str(exc_info.value)

    def test_validate_uuid_with_custom_field_name(self, repo):
        """Test _validate_uuid uses custom field name in error."""
        with pytest.raises(ValueError) as exc_info:
            repo._validate_uuid("invalid", field_name="document_id")

        assert "Invalid document_id" in str(exc_info.value)

    def test_validate_uuid_with_none(self, repo):
        """Test _validate_uuid raises ValueError for None."""
        with pytest.raises(ValueError) as exc_info:
            repo._validate_uuid(None)

        assert "Invalid id" in str(exc_info.value)

    def test_validate_uuid_with_empty_string(self, repo):
        """Test _validate_uuid raises ValueError for empty string."""
        with pytest.raises(ValueError) as exc_info:
            repo._validate_uuid("")

        assert "Invalid id" in str(exc_info.value)
