"""Tests for email service and monitoring functionality."""

import asyncio
import logging
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from alpine_bits_python.email_monitoring import (
    DailyReportScheduler,
    EmailAlertHandler,
    ErrorRecord,
)
from alpine_bits_python.email_service import EmailConfig, EmailService


class TestEmailConfig:
    """Tests for EmailConfig class."""

    def test_email_config_initialization(self):
        """Test basic email configuration initialization."""
        config = {
            "smtp": {
                "host": "smtp.example.com",
                "port": 587,
                "username": "test@example.com",
                "password": "password123",
                "use_tls": True,
                "use_ssl": False,
            },
            "from_address": "sender@example.com",
            "from_name": "Test Sender",
        }

        email_config = EmailConfig(config)

        assert email_config.smtp_host == "smtp.example.com"
        assert email_config.smtp_port == 587
        assert email_config.smtp_username == "test@example.com"
        assert email_config.smtp_password == "password123"
        assert email_config.use_tls is True
        assert email_config.use_ssl is False
        assert email_config.from_address == "sender@example.com"
        assert email_config.from_name == "Test Sender"

    def test_email_config_defaults(self):
        """Test email configuration with default values."""
        config = {}

        email_config = EmailConfig(config)

        assert email_config.smtp_host == "localhost"
        assert email_config.smtp_port == 587
        assert email_config.use_tls is True
        assert email_config.use_ssl is False
        assert email_config.from_address == "noreply@example.com"

    def test_email_config_tls_ssl_conflict(self):
        """Test that TLS and SSL cannot both be enabled."""
        config = {
            "smtp": {
                "use_tls": True,
                "use_ssl": True,
            }
        }

        with pytest.raises(ValueError, match="Cannot use both TLS and SSL"):
            EmailConfig(config)


class TestEmailService:
    """Tests for EmailService class."""

    @pytest.fixture
    def email_config(self):
        """Provide a test email configuration."""
        return EmailConfig(
            {
                "smtp": {
                    "host": "smtp.example.com",
                    "port": 587,
                    "username": "test@example.com",
                    "password": "password123",
                    "use_tls": True,
                },
                "from_address": "sender@example.com",
                "from_name": "Test Sender",
            }
        )

    @pytest.fixture
    def email_service(self, email_config):
        """Provide an EmailService instance."""
        return EmailService(email_config)

    @pytest.mark.asyncio
    async def test_send_email_success(self, email_service):
        """Test successful email sending."""
        with patch.object(email_service, "_send_smtp") as mock_smtp:
            result = await email_service.send_email(
                recipients=["test@example.com"],
                subject="Test Subject",
                body="Test body",
            )

            assert result is True
            assert mock_smtp.called

    @pytest.mark.asyncio
    async def test_send_email_no_recipients(self, email_service):
        """Test email sending with no recipients."""
        result = await email_service.send_email(
            recipients=[],
            subject="Test Subject",
            body="Test body",
        )

        assert result is False

    @pytest.mark.asyncio
    async def test_send_email_with_html(self, email_service):
        """Test email sending with HTML body."""
        with patch.object(email_service, "_send_smtp") as mock_smtp:
            result = await email_service.send_email(
                recipients=["test@example.com"],
                subject="Test Subject",
                body="Plain text body",
                html_body="<html><body>HTML body</body></html>",
            )

            assert result is True
            assert mock_smtp.called

    @pytest.mark.asyncio
    async def test_send_alert(self, email_service):
        """Test send_alert convenience method."""
        with patch.object(
            email_service, "send_email", new_callable=AsyncMock
        ) as mock_send:
            mock_send.return_value = True

            result = await email_service.send_alert(
                recipients=["test@example.com"],
                subject="Alert Subject",
                body="Alert body",
            )

            assert result is True
            mock_send.assert_called_once_with(
                ["test@example.com"], "Alert Subject", "Alert body"
            )

    @pytest.mark.asyncio
    async def test_send_daily_report(self, email_service):
        """Test daily report email generation and sending."""
        with patch.object(
            email_service, "send_email", new_callable=AsyncMock
        ) as mock_send:
            mock_send.return_value = True

            stats = {
                "total_reservations": 42,
                "new_customers": 15,
            }

            errors = [
                {
                    "timestamp": "2025-10-15 10:30:00",
                    "level": "ERROR",
                    "message": "Test error message",
                }
            ]

            result = await email_service.send_daily_report(
                recipients=["admin@example.com"],
                stats=stats,
                errors=errors,
            )

            assert result is True
            assert mock_send.called
            call_args = mock_send.call_args
            assert "admin@example.com" in call_args[0][0]
            assert "Daily Report" in call_args[0][1]


class TestErrorRecord:
    """Tests for ErrorRecord class."""

    def test_error_record_creation(self):
        """Test creating an ErrorRecord from a logging record."""
        log_record = logging.LogRecord(
            name="test.logger",
            level=logging.ERROR,
            pathname="/path/to/file.py",
            lineno=42,
            msg="Test error message",
            args=(),
            exc_info=None,
        )

        error_record = ErrorRecord(log_record)

        assert error_record.level == "ERROR"
        assert error_record.logger_name == "test.logger"
        assert error_record.message == "Test error message"
        assert error_record.module == "file"
        assert error_record.line_no == 42

    def test_error_record_to_dict(self):
        """Test converting ErrorRecord to dictionary."""
        log_record = logging.LogRecord(
            name="test.logger",
            level=logging.ERROR,
            pathname="/path/to/file.py",
            lineno=42,
            msg="Test error",
            args=(),
            exc_info=None,
        )

        error_record = ErrorRecord(log_record)
        error_dict = error_record.to_dict()

        assert error_dict["level"] == "ERROR"
        assert error_dict["message"] == "Test error"
        assert error_dict["line_no"] == 42
        assert "timestamp" in error_dict

    def test_error_record_format_plain_text(self):
        """Test formatting ErrorRecord as plain text."""
        log_record = logging.LogRecord(
            name="test.logger",
            level=logging.ERROR,
            pathname="/path/to/file.py",
            lineno=42,
            msg="Test error",
            args=(),
            exc_info=None,
        )

        error_record = ErrorRecord(log_record)
        formatted = error_record.format_plain_text()

        assert "ERROR" in formatted
        assert "Test error" in formatted
        assert "file:42" in formatted


class TestEmailAlertHandler:
    """Tests for EmailAlertHandler class."""

    @pytest.fixture
    def mock_email_service(self):
        """Provide a mock email service."""
        service = MagicMock(spec=EmailService)
        service.send_alert = AsyncMock(return_value=True)
        return service

    @pytest.fixture
    def handler_config(self):
        """Provide handler configuration."""
        return {
            "recipients": ["alert@example.com"],
            "error_threshold": 3,
            "buffer_minutes": 1,  # Short for testing
            "cooldown_minutes": 5,
            "log_levels": ["ERROR", "CRITICAL"],
        }

    @pytest.fixture
    def alert_handler(self, mock_email_service, handler_config):
        """Provide an EmailAlertHandler instance."""
        loop = asyncio.new_event_loop()
        handler = EmailAlertHandler(mock_email_service, handler_config, loop)
        yield handler
        loop.close()

    def test_handler_initialization(self, alert_handler, handler_config):
        """Test handler initialization."""
        assert alert_handler.error_threshold == 3
        assert alert_handler.buffer_minutes == 1
        assert alert_handler.cooldown_minutes == 5
        assert alert_handler.recipients == ["alert@example.com"]

    def test_handler_ignores_non_error_levels(self, alert_handler):
        """Test that handler ignores INFO and WARNING levels."""
        log_record = logging.LogRecord(
            name="test",
            level=logging.INFO,
            pathname="/test.py",
            lineno=1,
            msg="Info message",
            args=(),
            exc_info=None,
        )

        alert_handler.emit(log_record)

        # Should not buffer INFO messages
        assert len(alert_handler.error_buffer) == 0


class TestDailyReportScheduler:
    """Tests for DailyReportScheduler class."""

    @pytest.fixture
    def mock_email_service(self):
        """Provide a mock email service."""
        service = MagicMock(spec=EmailService)
        service.send_daily_report = AsyncMock(return_value=True)
        return service

    @pytest.fixture
    def scheduler_config(self):
        """Provide scheduler configuration."""
        return {
            "recipients": ["report@example.com"],
            "send_time": "08:00",
            "include_stats": True,
            "include_errors": True,
        }

    @pytest.fixture
    def scheduler(self, mock_email_service, scheduler_config):
        """Provide a DailyReportScheduler instance."""
        return DailyReportScheduler(mock_email_service, scheduler_config)

    def test_scheduler_initialization(self, scheduler, scheduler_config):
        """Test scheduler initialization."""
        assert scheduler.send_time == "08:00"
        assert scheduler.recipients == ["report@example.com"]
        assert scheduler.include_stats is True
        assert scheduler.include_errors is True

    def test_scheduler_log_error(self, scheduler):
        """Test logging errors for daily report."""
        error = {
            "timestamp": datetime.now().isoformat(),
            "level": "ERROR",
            "message": "Test error",
        }

        scheduler.log_error(error)

        assert len(scheduler._error_log) == 1
        assert scheduler._error_log[0]["message"] == "Test error"

    def test_scheduler_set_stats_collector(self, scheduler):
        """Test setting stats collector function."""

        async def mock_collector():
            return {"test": "stats"}

        scheduler.set_stats_collector(mock_collector)

        assert scheduler._stats_collector is mock_collector
