import pytest
import asyncio
from unittest.mock import AsyncMock

from app.email_throttling import DomainExtractor, ThrottlingConfiguration, EmailThrottler


@pytest.fixture
async def mock_email_sender():
    return AsyncMock()


@pytest.fixture
def domain_extractor():
    return DomainExtractor()


@pytest.fixture
def throttling_configuration():
    # Example configuration: 2 emails per second for gmail.com, 1 email per second for other domains
    config = {
        "gmail.com": {
            "rate_limit": 2,
            "period": 1
        },
        "default": {
            "rate_limit": 1,
            "period": 1
        }
    }
    return ThrottlingConfiguration(config)


@pytest.fixture
def email_throttler(throttling_configuration, domain_extractor, mock_email_sender):  # Corrected
    return EmailThrottler(throttling_configuration, domain_extractor, mock_email_sender)


async def send_email(email_throttler, recipient, subject, body):
    await email_throttler.send_email(recipient, subject, body)


@pytest.mark.asyncio
async def test_email_throttling_gmail(email_throttler, mock_email_sender):
    # Arrange
    recipients = ["test1@gmail.com", "test2@gmail.com", "test3@gmail.com"]
    subject = "Test Subject"
    body = "Test Body"

    # Act
    # Send emails in quick succession
    tasks = [send_email(email_throttler, recipient, subject, body) for recipient in recipients]
    await asyncio.gather(*tasks)

    # Assert
    # Ensure that the email sender was called the expected number of times (throttled).
    # With a rate limit of 2 per second for gmail.com, we expect 2 emails to be sent immediately,
    # and the third to be delayed.
    assert mock_email_sender.call_count == 3

    # Add more specific assertions to verify the throttling delay, if needed.
    # This would require more precise timing and possibly patching asyncio.sleep.

@pytest.mark.asyncio
async def test_email_throttling_yahoo(email_throttler, mock_email_sender):
    # Arrange
    recipients = ["test1@yahoo.com", "test2@yahoo.com"]
    subject = "Test Subject"
    body = "Test Body"

    # Act
    tasks = [send_email(email_throttler, recipient, subject, body) for recipient in recipients]
    await asyncio.gather(*tasks)

    # Assert
    # With a default rate limit of 1 per second, we expect 1 email to be sent immediately,
    # and the other to be delayed.
    assert mock_email_sender.call_count == 2


@pytest.mark.asyncio
async def test_email_throttling_mixed_domains(email_throttler, mock_email_sender):
    # Arrange
    recipients = ["test1@gmail.com", "test2@yahoo.com", "test3@gmail.com"]
    subject = "Test Subject"
    body = "Test Body"

    # Act
    tasks = [send_email(email_throttler, recipient, subject, body) for recipient in recipients]
    await asyncio.gather(*tasks)

    # Assert
    # Verify the number of calls to the mock_email_sender.
    # gmail.com: rate limit 2, yahoo.com: rate limit 1
    assert mock_email_sender.call_count == 3