from datetime import date
from decimal import Decimal
from unittest.mock import ANY, Mock

import pytest
from freezegun import freeze_time
from psycopg import Connection
from pytest_mock import MockFixture
from src.__types.errors import ClientError, ResourceDuplicatedError, ResourceNotFoundError
from src.__types.index import ContractProvider, Distribution, Tenant
from src.domain.entities.contract_event import ContractEvent
from src.domain.entities.quota import Quota
from src.domain.entities.quota_allocation import QuotaAllocation, QuotaAllocationStatus, QuotaAllocationType
from src.domain.services.tenant_service import TenantService
from src.infrastructure.contract_event_notifier import ContractEventNotifierProtocol
from src.infrastructure.credential_manager import CredentialManager
from src.infrastructure.repositories.quota_repository import QuotaRepository
from src.infrastructure.repositories.tenant_repository import TenantRepository
from src.usecases.contract_event_usecase import ContractEventUseCase


class TestContractEventUsecase:
    @pytest.fixture
    def mock_connection(self, mocker: MockFixture) -> Mock:
        return mocker.Mock(spec=Connection)

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

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

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

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

    @pytest.fixture
    def usecase(
        self,
        mock_connection: Mock,
        mock_tenant_service: Mock,
        mock_tenant_repository: Mock,
        mock_notifier: Mock,
    ) -> ContractEventUseCase:
        return ContractEventUseCase(
            connection=mock_connection,
            tenant_service=mock_tenant_service,
            tenant_repository=mock_tenant_repository,
            notifier=mock_notifier,
        )

    @pytest.fixture
    def contract_event(self) -> ContractEvent:
        return ContractEvent(
            legal_name="ダミー株式会社",
            distribution=Distribution.WHI_SFDC,
            customer_id="whi#dummy_1",
            contract_id="contract#dummy",
            type=QuotaAllocationType.BASE,
            amount=Decimal(1000),
            valid_from_date=date(2026, 1, 1),
            valid_to_date=date(2026, 12, 31),
        )

    class TestCreate:
        def test_create(
            self,
            mock_connection: Mock,
            mock_credential_manager: Mock,
            mocker: MockFixture,
        ) -> None:
            """TenantServiceとTenantRepositoryを生成してContractEventUseCaseを組み立てることを確認する。"""
            # Arrange
            expected_service = mocker.Mock(spec=TenantService)
            mock_tenant_service_create = mocker.patch(
                "src.usecases.contract_event_usecase.TenantService.create",
                return_value=expected_service,
            )
            expected_repo = mocker.Mock(spec=TenantRepository)
            mock_tenant_repo_class = mocker.patch(
                "src.usecases.contract_event_usecase.TenantRepository",
                return_value=expected_repo,
            )

            mock_notifier = mocker.Mock(spec=ContractEventNotifierProtocol)

            # Act
            actual = ContractEventUseCase.create(
                connection=mock_connection,
                credential_manager=mock_credential_manager,
                notifier=mock_notifier,
            )

            # Assert
            mock_tenant_service_create.assert_called_once_with(
                connection=mock_connection,
                credential_manager=mock_credential_manager,
            )
            mock_tenant_repo_class.assert_called_once_with(connection=mock_connection)
            assert isinstance(actual, ContractEventUseCase)
            assert actual.connection == mock_connection
            assert actual.tenant_service == expected_service
            assert actual.tenant_repository == expected_repo
            assert actual.notifier == mock_notifier

    class TestExecution:
        @pytest.mark.parametrize(
            ("provider_name", "distribution", "customer_id"),
            [
                ("whi_sfdc", Distribution.WHI_SFDC, "whi#dummy_1"),
                ("cydas_sfdc", Distribution.CYDAS_SFDC, "cydas#dummy_1"),
            ],
        )
        def test_success_when_tenant_exists(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_service: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
            provider_name: str,
            distribution: Distribution,
            customer_id: str,
        ) -> None:
            """テナントが既存の場合、取得してクォータ割り当てを追加することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name=provider_name,
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=distribution,
                customer_id=customer_id,
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mock_execute_tenant_rls = mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[])
            mock_quota_repo_class = mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act
            usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            mock_tenant_repository.get_by_customer_id.assert_called_once_with(
                distribution=distribution,
                customer_id=customer_id,
            )
            mock_tenant_service.issue.assert_not_called()
            mock_execute_tenant_rls.assert_called_once_with(connection=mock_connection, tenant_id=mock_tenant.id)
            mock_quota_repo_class.assert_called_once_with(tenant=mock_tenant, connection=mock_connection)
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#dummy")
            mock_notifier.notify_tenant_created.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_called_once_with(
                tenant=mock_tenant,
                allocation=ANY,
                contract_id="contract#dummy",
            )

        def test_success_when_tenant_not_found_and_issued(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_service: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """テナントが未存在の場合、発行後にクォータ割り当てを追加することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.side_effect = ResourceNotFoundError("not found")
            mock_tenant_service.issue.return_value = mock_tenant
            mock_execute_tenant_rls = mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act
            usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            mock_tenant_service.issue.assert_called_once_with(
                legal_name="ダミー株式会社",
                name=None,
                whi_customer_ids=["whi#dummy_1"],
                cydas_customer_ids=[],
            )
            mock_execute_tenant_rls.assert_called_once_with(connection=mock_connection, tenant_id=mock_tenant.id)
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#dummy")
            mock_notifier.notify_tenant_created.assert_called_once_with(
                tenant=mock_tenant,
                contract_event=contract_event,
            )
            mock_notifier.notify_quota_allocation_added.assert_called_once_with(
                tenant=mock_tenant,
                allocation=ANY,
                contract_id="contract#dummy",
            )

        def test_success_when_issue_raises_duplicate_and_tenant_fetched(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_service: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """テナント未存在時にissueで重複エラーが発生した場合、再度取得してクォータ割り当てを追加することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.side_effect = [
                ResourceNotFoundError("not found"),
                mock_tenant,
            ]
            mock_tenant_service.issue.side_effect = ResourceDuplicatedError("duplicate")
            mock_execute_tenant_rls = mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act
            usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            assert mock_tenant_repository.get_by_customer_id.call_count == 2
            mock_execute_tenant_rls.assert_called_once_with(connection=mock_connection, tenant_id=mock_tenant.id)
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#dummy")
            mock_notifier.notify_tenant_created.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_called_once_with(
                tenant=mock_tenant,
                allocation=ANY,
                contract_id="contract#dummy",
            )

        def test_raise_client_error_if_provider_is_unsupported(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_service: Mock,
            mock_tenant_repository: Mock,
            contract_event: ContractEvent,
            mocker: MockFixture,
            mock_tenant: Tenant,
        ) -> None:
            """未対応プロバイダ名の場合はClientErrorを送出し後続処理を行わないことを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="unsupported_provider",
            )
            mock_execute_tenant_rls = mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo_class = mocker.patch("src.usecases.contract_event_usecase.QuotaRepository")

            # Act / Assert
            with pytest.raises(ClientError, match="Unsupported contract provider name"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            mock_tenant_repository.get_by_customer_id.assert_not_called()
            mock_tenant_service.issue.assert_not_called()
            mock_execute_tenant_rls.assert_not_called()
            mock_quota_repo_class.assert_not_called()

        def test_raise_client_error_if_distribution_is_mismatched(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_service: Mock,
            mock_tenant_repository: Mock,
            mocker: MockFixture,
            mock_tenant: Tenant,
        ) -> None:
            """プロバイダとdistributionが不一致の場合はClientErrorを送出し後続処理を行わないことを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.CYDAS_SFDC,
                customer_id="cydas#dummy_1",
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_execute_tenant_rls = mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo_class = mocker.patch("src.usecases.contract_event_usecase.QuotaRepository")

            # Act / Assert
            with pytest.raises(ClientError, match="Distribution does not match contract provider"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            mock_tenant_repository.get_by_customer_id.assert_not_called()
            mock_tenant_service.issue.assert_not_called()
            mock_execute_tenant_rls.assert_not_called()
            mock_quota_repo_class.assert_not_called()

        def test_skip_when_quota_allocation_already_exists(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """クォータ割り当てが既に存在する場合、例外を上げずに正常終了することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[])
            mock_quota_repo.add_quota_allocation.side_effect = ResourceDuplicatedError(
                "QuotaAllocation for contract_id=contract#dummy, "
                "valid_from_date=2026-01-01, valid_to_date=2026-12-31 already exists.",
            )
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act　(例外が上がらないことを確認)
            usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#dummy")
            mock_notifier.notify_tenant_created.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_notify_tenant_creation_failed_when_issue_raises_unexpected_error(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_service: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """テナント発行時に予期しない例外が発生した場合、失敗通知を送って例外を再送出することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            error = RuntimeError("unexpected error")
            mock_tenant_repository.get_by_customer_id.side_effect = ResourceNotFoundError("not found")
            mock_tenant_service.issue.side_effect = error
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mocker.patch("src.usecases.contract_event_usecase.QuotaRepository")

            # Act / Assert
            with pytest.raises(RuntimeError, match="unexpected error"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            mock_notifier.notify_tenant_creation_failed.assert_called_once_with(
                contract_event=contract_event,
                error_message=str(error),
            )
            mock_notifier.notify_tenant_created.assert_not_called()

        def test_notify_quota_allocation_failed_when_add_quota_raises_unexpected_error(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """クォータ割り当て時に予期しない例外が発生した場合、失敗通知を送って例外を再送出することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#dummy",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            error = RuntimeError("unexpected error")
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[])
            mock_quota_repo.add_quota_allocation.side_effect = error
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with pytest.raises(RuntimeError, match="unexpected error"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            mock_notifier.notify_quota_allocation_failed.assert_called_once_with(
                tenant=mock_tenant,
                contract_event=contract_event,
                error_message=str(error),
            )
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_raise_client_error_when_active_base_allocation_already_exists(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """ACTIVEな基本契約が既に存在し、追加する契約もACTIVEになる場合、拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            existing_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-07-02 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_raise_client_error_when_scheduled_base_allocation_overlaps(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """SCHEDULEDな基本契約が既に存在し、追加する契約の期間が重複する場合、拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2027, 4, 1),
                valid_to_date=date(2028, 3, 31),
            )
            existing_scheduled_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.SCHEDULED,
                amount=Decimal(1000),
                used=Decimal(0),
                valid_from_date=date(2027, 1, 1),
                valid_to_date=date(2027, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_scheduled_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-07-02 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_allow_base_allocation_when_scheduled_base_exists(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """ACTIVEな基本契約が存在しても、追加する契約の期間が重複しない場合は許容することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2027, 1, 1),
                valid_to_date=date(2027, 12, 31),
            )
            existing_active_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_active_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act
            with freeze_time("2026-07-02 12:00:00"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#new_base")
            mock_notifier.notify_quota_allocation_added.assert_called_once()

        def test_raise_client_error_when_new_base_starts_on_existing_end_date(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """新規の開始日が既存の終了日と同日の場合、同日に両方ACTIVEとなるため拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 12, 31),
                valid_to_date=date(2027, 12, 31),
            )
            existing_active_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_active_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-07-02 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_raise_client_error_when_new_base_ends_on_existing_start_date(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """新規の終了日が既存SCHEDULEDの開始日と同日の場合、同日に両方ACTIVEとなるため拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2027, 1, 1),
            )
            existing_scheduled_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.SCHEDULED,
                amount=Decimal(1000),
                used=Decimal(0),
                valid_from_date=date(2027, 1, 1),
                valid_to_date=date(2027, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_scheduled_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-07-02 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_allow_base_allocation_when_new_base_ends_before_existing_start_date(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """新規の終了日が既存SCHEDULEDの開始日の前日の場合、期間が重複しないため許容することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            existing_scheduled_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.SCHEDULED,
                amount=Decimal(1000),
                used=Decimal(0),
                valid_from_date=date(2027, 1, 1),
                valid_to_date=date(2027, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_scheduled_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act
            with freeze_time("2026-07-02 12:00:00"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#new_base")
            mock_notifier.notify_quota_allocation_added.assert_called_once()

        def test_allow_base_allocation_when_no_active_or_scheduled_base_exists(
            self,
            usecase: ContractEventUseCase,
            mock_connection: Mock,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """有効な基本契約が存在しない場合、BASEのクォータ割り当て追加が正常に行われることを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act
            with freeze_time("2026-07-02 12:00:00"):
                usecase.execute(contract_provider=contract_provider, contract_event=contract_event)

            # Assert
            mock_quota_repo.add_quota_allocation.assert_called_once_with(ANY, contract_id="contract#new_base")
            mock_notifier.notify_quota_allocation_added.assert_called_once()

        def test_raise_client_error_when_new_base_overlaps_tail_of_existing(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """後方交差: 新規が既存の途中から開始し後ろに突き出る場合、拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 7, 1),
                valid_to_date=date(2026, 12, 31),
            )
            existing_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 4, 1),
                valid_to_date=date(2026, 9, 30),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-07-02 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_raise_client_error_when_new_base_overlaps_head_of_existing(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """前方交差: 新規が既存の手前から始まり途中で終わる場合、拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 6, 30),
            )
            existing_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 4, 1),
                valid_to_date=date(2026, 9, 30),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-04-15 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_raise_client_error_when_new_base_is_contained_within_existing(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """内包: 新規が既存の期間内に完全に収まる場合、拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 5, 1),
                valid_to_date=date(2026, 8, 31),
            )
            existing_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 4, 1),
                valid_to_date=date(2026, 9, 30),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-05-01 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()

        def test_raise_client_error_when_new_base_encompasses_existing(
            self,
            usecase: ContractEventUseCase,
            mock_tenant_repository: Mock,
            mock_notifier: Mock,
            mock_tenant: Tenant,
            mocker: MockFixture,
        ) -> None:
            """超過: 新規が既存の期間を完全に包み込む場合、拒否することを確認する。"""
            # Arrange
            contract_provider = ContractProvider(
                id=mock_tenant.id,
                application_client_id="application_client#dummy",
                name="whi_sfdc",
            )
            contract_event = ContractEvent(
                legal_name="ダミー株式会社",
                distribution=Distribution.WHI_SFDC,
                customer_id="whi#dummy_1",
                contract_id="contract#new_base",
                type=QuotaAllocationType.BASE,
                amount=Decimal(1000),
                valid_from_date=date(2026, 1, 1),
                valid_to_date=date(2026, 12, 31),
            )
            existing_base = QuotaAllocation(
                id=mock_tenant.id,
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(1000),
                used=Decimal(500),
                valid_from_date=date(2026, 4, 1),
                valid_to_date=date(2026, 9, 30),
            )
            mock_tenant_repository.get_by_customer_id.return_value = mock_tenant
            mocker.patch("src.usecases.contract_event_usecase.execute_tenant_rls")
            mock_quota_repo = mocker.Mock(spec=QuotaRepository)
            mock_quota_repo.get.return_value = Quota(allocations=[existing_base])
            mocker.patch(
                "src.usecases.contract_event_usecase.QuotaRepository",
                return_value=mock_quota_repo,
            )

            # Act / Assert
            with freeze_time("2026-04-15 12:00:00"):
                with pytest.raises(ClientError, match="A base allocation with an overlapping period already exists"):
                    usecase.execute(contract_provider=contract_provider, contract_event=contract_event)
            mock_quota_repo.add_quota_allocation.assert_not_called()
            mock_notifier.notify_quota_allocation_added.assert_not_called()
