import datetime
import uuid
from decimal import Decimal

from src.domain.entities.quota import Quota
from src.domain.entities.quota_allocation import QuotaAllocation, QuotaAllocationStatus, QuotaAllocationType


class TestQuota:
    class TestSerialization:
        def test(self):
            """Quotaのシリアライズが正しいことを確認する"""
            expected_limit = 10000
            expected_used = 2001
            expected_remaining = 7999

            quota = Quota(
                allocations=[
                    QuotaAllocation(
                        id=uuid.uuid4(),
                        type=QuotaAllocationType.BASE,
                        status=QuotaAllocationStatus.ACTIVE,
                        amount=Decimal(10000),
                        used=Decimal("2000.1"),
                        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),
                    ),
                ],
            )

            serialized = quota.model_dump()
            assert serialized["limit"] == expected_limit
            assert serialized["used"] == expected_used
            assert serialized["remaining"] == expected_remaining

    class TestCalculation:
        def test_with_only_active(self):
            """有効なクォータ割り当てのみで計算されることを確認する"""
            expected_limit = Decimal(20000)
            expected_used = Decimal(5000)
            expected_remaining = Decimal(15000)
            expected_is_exceeded = False
            actual_quota_allocation_1 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                used=Decimal(2000),
                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),
            )
            actual_quota_allocation_2 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.TOP_UP,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                used=Decimal(3000),
                valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2025, month=6, day=30, tzinfo=datetime.timezone.utc),
            )
            actual_quota = Quota(
                allocations=[
                    actual_quota_allocation_1,
                    actual_quota_allocation_2,
                ],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is expected_is_exceeded

        def test_with_schedule_and_expired(self):
            expected_limit = Decimal(30000)
            expected_used = Decimal(7000)
            expected_remaining = Decimal(23000)
            actual_quota_allocation_1 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                used=Decimal(2000),
                valid_from_date=datetime.datetime(year=2024, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2024, month=12, day=31, tzinfo=datetime.timezone.utc),
            )
            actual_quota_allocation_2 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.TOP_UP,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                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=6, day=30, tzinfo=datetime.timezone.utc),
            )
            actual_quota_allocation_3 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.TOP_UP,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                used=Decimal(5000),
                valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2025, month=6, day=30, tzinfo=datetime.timezone.utc),
            )
            actual_quota_allocation_4 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.EXCEEDED,
                amount=Decimal(10000),
                used=Decimal(10000),
                valid_from_date=datetime.datetime(year=2024, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2024, month=12, day=31, tzinfo=datetime.timezone.utc),
            )
            actual_quota_allocation_5 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.SCHEDULED,
                amount=Decimal(10000),
                used=Decimal(0),
                valid_from_date=datetime.datetime(year=2026, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2026, month=12, day=31, tzinfo=datetime.timezone.utc),
            )
            actual_quota = Quota(
                allocations=[
                    actual_quota_allocation_1,  # 計上される
                    actual_quota_allocation_2,  # 計上される
                    actual_quota_allocation_3,  # 計上される
                    actual_quota_allocation_4,  # 計上されない
                    actual_quota_allocation_5,  # 計上されない
                ],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is False

        def test_is_exceeded(self):
            """クォータが超過している場合、is_exceededがTrueになることを確認する"""
            expected_limit = Decimal(10000)
            expected_used = Decimal(10000)
            expected_remaining = Decimal(0)
            expected_is_exceeded = True
            actual_quota_allocation = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                used=Decimal(10000),
                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),
            )
            actual_quota = Quota(
                allocations=[
                    actual_quota_allocation,
                ],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is expected_is_exceeded

        def test_is_exceeded_with_schedule(self):
            expected_limit = Decimal(10000)
            expected_used = Decimal(10000)
            expected_remaining = Decimal(0)
            expected_is_exceeded = True
            actual_quota_allocation_1 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal(10000),
                used=Decimal(10000),
                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),
            )
            actual_quota_allocation_2 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.SCHEDULED,
                amount=Decimal(10000),
                used=Decimal(0),
                valid_from_date=datetime.datetime(year=2026, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2026, month=12, day=31, tzinfo=datetime.timezone.utc),
            )
            actual_quota = Quota(
                allocations=[
                    actual_quota_allocation_1,
                    actual_quota_allocation_2,
                ],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is expected_is_exceeded

        def test_with_no_allocation(self):
            """クォータ割り当てが無い場合、全ての値が0になることを確認する"""
            expected_limit = Decimal(0)
            expected_used = Decimal(0)
            expected_remaining = Decimal(0)
            expected_is_exceeded = True
            actual_quota = Quota(
                allocations=[],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is expected_is_exceeded

        def test_decimal_with_same_digits(self):
            """Decimal型の値が正しく計算されることを確認する"""
            expected_limit = Decimal("999.777")
            expected_used = Decimal("653.566")
            expected_remaining = Decimal("346.211")
            expected_is_exceeded = False
            actual_quota_allocation_1 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal("333.259"),
                used=Decimal("200.123"),
                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),
            )
            actual_quota_allocation_2 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.TOP_UP,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal("333.259"),
                used=Decimal("200.222"),
                valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2025, month=6, day=30, tzinfo=datetime.timezone.utc),
            )
            actual_quota_allocation_3 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.TOP_UP,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal("333.259"),
                used=Decimal("253.221"),
                valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2025, month=6, day=30, tzinfo=datetime.timezone.utc),
            )
            actual_quota = Quota(
                allocations=[
                    actual_quota_allocation_1,
                    actual_quota_allocation_2,
                    actual_quota_allocation_3,
                ],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is expected_is_exceeded

        def test_decimal_with_different_digits(self):
            """Decimal型の値が正しく計算されることを確認する"""
            expected_limit = Decimal("666.663300000000")
            expected_used = Decimal("444.442200000000")
            expected_remaining = Decimal("222.221100000000")
            expected_is_exceeded = False
            actual_quota_allocation_1 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.BASE,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal("333.3333"),
                used=Decimal("222.2222"),
                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),
            )
            actual_quota_allocation_2 = QuotaAllocation(
                id=uuid.uuid4(),
                type=QuotaAllocationType.TOP_UP,
                status=QuotaAllocationStatus.ACTIVE,
                amount=Decimal("333.33"),
                used=Decimal("222.22"),
                valid_from_date=datetime.datetime(year=2025, month=1, day=1, tzinfo=datetime.timezone.utc),
                valid_to_date=datetime.datetime(year=2025, month=6, day=30, tzinfo=datetime.timezone.utc),
            )
            actual_quota = Quota(
                allocations=[
                    actual_quota_allocation_1,
                    actual_quota_allocation_2,
                ],
            )
            assert actual_quota.limit == expected_limit
            assert actual_quota.used == expected_used
            assert actual_quota.remaining == expected_remaining
            assert actual_quota.is_exceeded is expected_is_exceeded
