from unittest.mock import Mock

import pytest
from pytest_mock import MockFixture
from src.__types.index import Client, ClientTenant, ClientTenantStatus, Distribution, PendingClientTenant
from src.domain.services.client_tenant_service import ClientTenantService
from src.infrastructure.repositories.client_tenant_repository import ClientTenantRepository
from src.infrastructure.repositories.pending_client_tenant_repository import PendingClientTenantRepository
from src.usecases.client_tenant_usecase import ClientTenantListItem, ClientTenantUseCase


class TestClientTenantUseCase:
    @pytest.fixture
    def mock_client_tenant_repository(self, mocker: MockFixture) -> Mock:
        return mocker.Mock(spec=ClientTenantRepository)

    @pytest.fixture
    def mock_pending_client_tenant_repository(self, mocker: MockFixture) -> Mock:
        return mocker.Mock(spec=PendingClientTenantRepository)

    @pytest.fixture
    def mock_client_tenant_service(self, mocker: MockFixture) -> Mock:
        return mocker.Mock(spec=ClientTenantService)

    @pytest.fixture
    def usecase(
        self,
        mock_client_tenant_repository: Mock,
        mock_pending_client_tenant_repository: Mock,
        mock_client_tenant_service: Mock,
    ) -> ClientTenantUseCase:
        return ClientTenantUseCase(
            client_tenant_repository=mock_client_tenant_repository,
            pending_client_tenant_repository=mock_pending_client_tenant_repository,
            client_tenant_service=mock_client_tenant_service,
        )

    class TestGet:
        def test_with_client_id_is_null(
            self,
            usecase: ClientTenantUseCase,
            mock_client_tenant_repository: Mock,
            mock_pending_client_tenant_repository: Mock,
            mock_client: Client,
            mock_client_tenant: ClientTenant,
            mock_pending_client_tenant: PendingClientTenant,
        ) -> None:
            """client_idを持つ/持たないClientTenantを取得できることを確認する。"""
            # Arrange
            client_tenants = [
                mock_client_tenant,
            ]
            pending_client_tenants = [
                mock_pending_client_tenant,
            ]
            mock_client_tenant_repository.find.return_value = client_tenants
            mock_pending_client_tenant_repository.find.return_value = pending_client_tenants
            # Act
            actual = usecase.get(client=mock_client)

            # Assert
            assert actual == [
                ClientTenantListItem(
                    name=mock_client_tenant.name,
                    status=ClientTenantStatus.ACTIVE,
                ),
                ClientTenantListItem(
                    name=mock_pending_client_tenant.name,
                    status=ClientTenantStatus.PENDING,
                ),
            ]
            mock_client_tenant_repository.find.assert_called_once_with(client_id=mock_client.id)

    class TestAdd:
        def test_tenant_exists(
            self,
            usecase: ClientTenantUseCase,
            mock_client_tenant_service: Mock,
            mock_client: Client,
        ) -> None:
            """Tenantと紐付け可能な場合にClientTenantを作成できることを確認する。"""
            # Act
            usecase.add(
                client=mock_client,
                name="dummy_tenant_name",
                distribution=Distribution.WHI_SFDC,
                customer_id="dummy_customer_id",
                base_url="https://dummy.com",
            )

            # Assert
            mock_client_tenant_service.add.assert_called_once_with(
                client=mock_client,
                name="dummy_tenant_name",
                distribution=Distribution.WHI_SFDC,
                customer_id="dummy_customer_id",
                base_url="https://dummy.com",
            )

        def test_tenant_not_found(
            self,
            usecase: ClientTenantUseCase,
            mock_client_tenant_service: Mock,
            mock_client: Client,
        ) -> None:
            """Tenantと紐付け不可能な場合にClientTenantを作成できることを確認する。"""
            # Arrange
            mock_client_tenant_service.add.return_value = None

            # Act
            usecase.add(
                client=mock_client,
                name="dummy_tenant_name",
                distribution=Distribution.WHI_SFDC,
                customer_id="missing",
                base_url="https://dummy.com",
            )

            # Assert
            mock_client_tenant_service.add.assert_called_once_with(
                client=mock_client,
                name="dummy_tenant_name",
                distribution=Distribution.WHI_SFDC,
                customer_id="missing",
                base_url="https://dummy.com",
            )
