import datetime
import uuid
from decimal import Decimal
from unittest.mock import Mock

import pytest
from pytest_mock import MockFixture
from src.domain.entities.quota import Quota
from src.domain.entities.quota_allocation import QuotaAllocation, QuotaAllocationStatus, QuotaAllocationType
from src.infrastructure.repositories.quota_repository import QuotaRepository
from src.usecases.quota_usecase import QuotaUseCase


class TestQuotaUsecase:
    @pytest.fixture
    def mock_quota_repository(self, mocker: MockFixture):
        return mocker.Mock(spec=QuotaRepository)

    @pytest.fixture
    def usecase(self, mock_quota_repository: QuotaRepository):
        return QuotaUseCase(quota_repository=mock_quota_repository)

    class TestCheck:
        def test_is_exceeded(self, usecase: QuotaUseCase, mock_quota_repository: Mock):
            """クォータが取得できることを確認する"""
            # Arrange
            mock_quota_repository.get.return_value.is_exceeded = True
            # Act
            actual_result = usecase.check()
            # Assert
            assert actual_result is False

    class TestGet:
        def test_get(self, usecase: QuotaUseCase, mock_quota_repository: Mock):
            """クォータが取得できることを確認する"""
            # Arrange
            expected_quota = Quota(
                allocations=[],
            )
            mock_quota_repository.get.return_value = expected_quota
            # Act
            actual_quota = usecase.get()
            # Assert
            assert actual_quota == expected_quota

    class TestGetAllocations:
        def test_get_allocations(self, usecase: QuotaUseCase, mock_quota_repository: Mock):
            """指定されたステータスのクォータ割り当ておよび使用中のクォータ割り当てIDが取得できることを確認する"""
            # Arrange
            expected_in_use_allocation_id = uuid.uuid4()
            expected_allocations = [
                QuotaAllocation(
                    id=uuid.uuid4(),
                    type=QuotaAllocationType.BASE,
                    status=QuotaAllocationStatus.ACTIVE,
                    amount=Decimal(1000),
                    used=Decimal(0),
                    valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                    valid_to_date=datetime.datetime(year=2025, month=12, day=31, tzinfo=datetime.timezone.utc),
                ),
                QuotaAllocation(
                    id=expected_in_use_allocation_id,  # 随時拡張契約優先
                    type=QuotaAllocationType.TOP_UP,
                    status=QuotaAllocationStatus.ACTIVE,
                    amount=Decimal(1000),
                    used=Decimal(500),
                    valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                    valid_to_date=datetime.datetime(year=2025, month=12, day=31, tzinfo=datetime.timezone.utc),
                ),
            ]
            mock_quota_repository.get.return_value = Quota(
                allocations=[
                    *expected_allocations,
                    QuotaAllocation(
                        id=expected_in_use_allocation_id,
                        type=QuotaAllocationType.TOP_UP,
                        status=QuotaAllocationStatus.EXCEEDED,
                        amount=Decimal(1000),
                        used=Decimal(1000),
                        valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                        valid_to_date=datetime.datetime(year=2025, month=12, day=31, tzinfo=datetime.timezone.utc),
                    ),
                ],  # 無関係な割り当ても含む
            )
            # Act
            actual_in_use_allocation_id, actual_allocations = usecase.get_allocations(
                status=[QuotaAllocationStatus.ACTIVE],
            )
            # Assert
            assert actual_in_use_allocation_id == expected_in_use_allocation_id
            assert actual_allocations == expected_allocations
