import uuid
from unittest.mock import Mock

import pytest
from pytest_mock import MockFixture
from src.__types.index import CountedList, User
from src.infrastructure.repositories.user_repository import UserRepository
from src.usecases.user_query_usecase import UserQueryUseCase


class TestUserQueryUseCase:
    @pytest.fixture
    def mock_user_repository(self, mocker: MockFixture) -> Mock:
        return mocker.Mock(spec=UserRepository)

    @pytest.fixture
    def usecase(self, mock_user_repository: Mock) -> UserQueryUseCase:
        return UserQueryUseCase(user_repository=mock_user_repository)

    class TestListAll:
        def test_returns_all_users(
            self,
            usecase: UserQueryUseCase,
            mock_user_repository: Mock,
        ) -> None:
            """全ユーザーリストを返すことを確認する。"""
            # Arrange
            users = [
                User(id=uuid.uuid4(), display_id="0001", display_name="ユーザー1", email="user1@test.com"),
                User(id=uuid.uuid4(), display_id="0002", display_name="ユーザー2", email=None),
            ]
            mock_user_repository.find_all.return_value = users

            # Act
            result = usecase.list_all()

            # Assert
            assert result == users
            mock_user_repository.find_all.assert_called_once()

        def test_returns_empty_list(
            self,
            usecase: UserQueryUseCase,
            mock_user_repository: Mock,
        ) -> None:
            """ユーザーが存在しない場合に空リストを返すことを確認する。"""
            # Arrange
            mock_user_repository.find_all.return_value = []

            # Act
            result = usecase.list_all()

            # Assert
            assert result == []

    class TestList:
        def test_returns_paginated_list(
            self,
            usecase: UserQueryUseCase,
            mock_user_repository: Mock,
        ) -> None:
            """ページネーション付きのユーザーリストを返すことを確認する。"""
            # Arrange
            users = [
                User(id=uuid.uuid4(), display_id="0001", display_name="ユーザー1", email="user1@test.com"),
                User(id=uuid.uuid4(), display_id="0002", display_name="ユーザー2", email=None),
            ]
            mock_user_repository.count.return_value = 5
            mock_user_repository.find.return_value = users

            # Act
            result = usecase.list(page=2, page_size=2)

            # Assert
            assert result == CountedList(total=5, items=users)
            mock_user_repository.count.assert_called_once()
            mock_user_repository.find.assert_called_once_with(limit=2, offset=2)

        def test_returns_empty_list(
            self,
            usecase: UserQueryUseCase,
            mock_user_repository: Mock,
        ) -> None:
            """ユーザーが存在しない場合に空リストを返すことを確認する。"""
            # Arrange
            mock_user_repository.count.return_value = 0
            mock_user_repository.find.return_value = []

            # Act
            result = usecase.list(page=1, page_size=10)

            # Assert
            assert result == CountedList(total=0, items=[])

    class TestGet:
        def test_returns_user(
            self,
            usecase: UserQueryUseCase,
            mock_user_repository: Mock,
        ) -> None:
            """指定IDのユーザーを取得できることを確認する。"""
            # Arrange
            user_id = uuid.uuid4()
            expected = User(id=user_id, display_id="0001", display_name="ユーザー1", email="user1@test.com")
            mock_user_repository.get.return_value = expected

            # Act
            result = usecase.get(user_id)

            # Assert
            assert result == expected
            mock_user_repository.get.assert_called_once_with(user_id)
