from decimal import Decimal
from unittest.mock import Mock

import pytest
from pytest_mock import MockFixture
from src.domain.entities.attention import AttentionSeverity, AttentionType, LastMonthUsageParameters
from src.infrastructure.repositories.quota_repository import QuotaRepository
from src.infrastructure.repositories.quota_usage_repository import QuotaUsageRepository
from src.infrastructure.repositories.usage_event_repository import UsageEventRepository
from src.usecases.attention_usecase import AttentionUseCase


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

    @pytest.fixture
    def mock_usage_event_repository(self, mocker: MockFixture):
        return mocker.Mock(spec=UsageEventRepository)

    @pytest.fixture
    def mock_quota_usage_repository(self, mocker: MockFixture):
        return mocker.Mock(spec=QuotaUsageRepository)

    @pytest.fixture
    def usecase(
        self,
        mock_quota_repository: QuotaRepository,
        mock_usage_event_repository: UsageEventRepository,
        mock_quota_usage_repository: QuotaUsageRepository,
    ):
        return AttentionUseCase(
            quota_repository=mock_quota_repository,
            usage_event_repository=mock_usage_event_repository,
            quota_usage_repository=mock_quota_usage_repository,
        )

    class TestGet:
        def test_quota_exceeded(
            self,
            usecase: AttentionUseCase,
            mock_quota_repository: Mock,
        ):
            """クォータが超過している場合、重大度最大のアテンションが返ることを確認する"""
            # Arrange
            mock_quota_repository.get.return_value.is_exceeded = True
            # Act
            actual_attentions = usecase.get()
            # Assert
            assert len(actual_attentions) == 1
            assert actual_attentions[0].severity == AttentionSeverity.HIGH
            assert actual_attentions[0].type == AttentionType.EXCEEDED
            assert actual_attentions[0].parameters is None

        def test_last_month_usage_exceeded(
            self,
            usecase: AttentionUseCase,
            mock_quota_repository: Mock,
            mock_quota_usage_repository: Mock,
        ):
            """前月の使用量がクォータの月当たり残量を超えている場合、重大度中のアテンションが返ることを確認する"""
            # Arrange
            mock_quota_repository.get.return_value.is_exceeded = False
            mock_quota_repository.get.return_value.remaining_per_remaining_month = Decimal(10000)
            mock_quota_usage_repository.get_last_month_usage.return_value = Decimal(15000)
            # Act
            actual_attentions = usecase.get()
            # Assert
            assert len(actual_attentions) == 1
            assert actual_attentions[0].severity == AttentionSeverity.MEDIUM
            assert actual_attentions[0].type == AttentionType.LAST_MONTH_USAGE
            assert actual_attentions[0].parameters == LastMonthUsageParameters(last_month_usage=Decimal(15000))

        def test_last_month_usage_not_exceeded(
            self,
            usecase: AttentionUseCase,
            mock_quota_repository: Mock,
            mock_quota_usage_repository: Mock,
        ):
            """前月の使用量がクォータの月当たり残量を超えていない場合、重大度小のアテンションが返ることを確認する"""
            # Arrange
            mock_quota_repository.get.return_value.is_exceeded = False
            mock_quota_repository.get.return_value.remaining_per_remaining_month = Decimal(20000)
            mock_quota_usage_repository.get_last_month_usage.return_value = Decimal(15000)
            # Act
            actual_attentions = usecase.get()
            # Assert
            assert len(actual_attentions) == 1
            assert actual_attentions[0].severity == AttentionSeverity.LOW
            assert actual_attentions[0].type == AttentionType.LAST_MONTH_USAGE
            assert actual_attentions[0].parameters == LastMonthUsageParameters(last_month_usage=Decimal(15000))
