# app/modules/messaging/tests/unit/test_email_log_service.py
"""Unit tests for EmailTemplateService email log (audit) methods."""

import uuid
from datetime import datetime, timedelta

import pytest

from app.modules.messaging.models import (
    EmailCategory,
    EmailLog,
    EmailStatus,
    EmailTemplate,
)
from app.modules.messaging.services.email_template_service import EmailTemplateService

# =============================================================================
# FIXTURES
# =============================================================================


@pytest.fixture
def email_template(db):
    """Create a test email template."""
    uid = uuid.uuid4().hex[:8]
    template = EmailTemplate(
        code=f"test_template_{uid}",
        language="en",
        name="Test Template",
        description="A test template",
        category=EmailCategory.SYSTEM.value,
        subject="Test subject {{ name }}",
        body_html="<p>Hello {{ name }}</p>",
        body_text="Hello {{ name }}",
        is_active=True,
    )
    db.add(template)
    db.commit()
    db.refresh(template)
    return template


@pytest.fixture
def email_logs(db, email_template):
    """Create a batch of email logs with various statuses."""
    logs = []
    statuses = [
        (EmailStatus.SENT, 3),
        (EmailStatus.FAILED, 2),
        (EmailStatus.PENDING, 1),
        (EmailStatus.DELIVERED, 2),
        (EmailStatus.BOUNCED, 1),
    ]

    for status, count in statuses:
        for i in range(count):
            uid = uuid.uuid4().hex[:8]
            logs.append(EmailLog(
                template_code=email_template.code,
                recipient_email=f"user_{uid}@example.com",
                recipient_name=f"User {uid}",
                subject=f"Test subject {uid}",
                body_html=f"<p>Hello {uid}</p>",
                body_text=f"Hello {uid}",
                from_email="noreply@orion.lu",
                from_name="Orion",
                status=status.value,
                provider="debug",
                sent_at=datetime.utcnow() if status != EmailStatus.PENDING else None,
            ))

    db.add_all(logs)
    db.commit()
    for log in logs:
        db.refresh(log)
    return logs


@pytest.fixture
def multi_template_logs(db):
    """Create logs from multiple templates for stats testing."""
    logs = []
    templates = ["signup_welcome", "order_confirmation", "password_reset"]

    for tpl_code in templates:
        for i in range(2):
            uid = uuid.uuid4().hex[:8]
            logs.append(EmailLog(
                template_code=tpl_code,
                recipient_email=f"{tpl_code}_{uid}@example.com",
                subject=f"{tpl_code} email",
                from_email="noreply@orion.lu",
                status=EmailStatus.SENT.value,
                provider="debug",
                sent_at=datetime.utcnow(),
            ))

    # Add one failed log
    logs.append(EmailLog(
        template_code="password_reset",
        recipient_email="failed@example.com",
        subject="Failed email",
        from_email="noreply@orion.lu",
        status=EmailStatus.FAILED.value,
        provider="debug",
        error_message="SMTP connection refused",
    ))

    db.add_all(logs)
    db.commit()
    for log in logs:
        db.refresh(log)
    return logs


# =============================================================================
# GET EMAIL LOGS
# =============================================================================


@pytest.mark.unit
@pytest.mark.email
class TestGetEmailLogs:
    """Tests for get_email_logs() service method."""

    def test_returns_paginated_logs(self, db, email_logs):
        """Logs are returned with correct total count."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(skip=0, limit=50)

        assert total == 9  # 3 sent + 2 failed + 1 pending + 2 delivered + 1 bounced
        assert len(items) == 9

    def test_pagination_limit(self, db, email_logs):
        """Limit restricts number of returned items."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(skip=0, limit=3)

        assert total == 9
        assert len(items) == 3

    def test_pagination_skip(self, db, email_logs):
        """Skip offsets the results."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(skip=5, limit=50)

        assert total == 9
        assert len(items) == 4

    def test_filter_by_status(self, db, email_logs):
        """Filter by status returns matching logs only."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(
            filters={"status": "sent"}, skip=0, limit=50
        )

        assert total == 3
        assert all(item["status"] == "sent" for item in items)

    def test_filter_by_status_failed(self, db, email_logs):
        """Filter by failed status."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(
            filters={"status": "failed"}, skip=0, limit=50
        )

        assert total == 2
        assert all(item["status"] == "failed" for item in items)

    def test_filter_by_template_code(self, db, email_logs, email_template):
        """Filter by template_code returns matching logs."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(
            filters={"template_code": email_template.code}, skip=0, limit=50
        )

        assert total == 9
        assert all(item["template_code"] == email_template.code for item in items)

    def test_filter_by_recipient_search(self, db, email_logs):
        """Search filter matches recipient email substring."""
        # Get the first log's email for a search term
        service = EmailTemplateService(db)
        first_email = email_logs[0].recipient_email

        items, total = service.get_email_logs(
            filters={"search": first_email}, skip=0, limit=50
        )

        assert total >= 1
        assert any(first_email in item["recipient_email"] for item in items)

    def test_filter_by_date_range(self, db, email_logs):
        """Date range filter works."""
        service = EmailTemplateService(db)
        yesterday = (datetime.utcnow() - timedelta(days=1)).isoformat()
        tomorrow = (datetime.utcnow() + timedelta(days=1)).isoformat()

        items, total = service.get_email_logs(
            filters={"date_from": yesterday, "date_to": tomorrow},
            skip=0, limit=50,
        )

        assert total == 9

    def test_combined_filters(self, db, email_logs, email_template):
        """Multiple filters can be combined."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(
            filters={
                "status": "sent",
                "template_code": email_template.code,
            },
            skip=0, limit=50,
        )

        assert total == 3
        assert all(item["status"] == "sent" for item in items)

    def test_empty_result(self, db, email_logs):
        """Non-matching filter returns empty list."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(
            filters={"search": "nonexistent_address_xyz@nowhere.test"},
            skip=0, limit=50,
        )

        assert total == 0
        assert items == []

    def test_log_item_structure(self, db, email_logs):
        """Returned items contain expected fields (no body content)."""
        service = EmailTemplateService(db)
        items, _ = service.get_email_logs(skip=0, limit=1)

        item = items[0]
        assert "id" in item
        assert "recipient_email" in item
        assert "subject" in item
        assert "status" in item
        assert "template_code" in item
        assert "created_at" in item
        # Body fields should NOT be in list items
        assert "body_html" not in item
        assert "body_text" not in item

    def test_no_filters(self, db, email_logs):
        """Calling with None filters returns all logs."""
        service = EmailTemplateService(db)
        items, total = service.get_email_logs(filters=None, skip=0, limit=50)

        assert total == 9


# =============================================================================
# GET EMAIL LOG DETAIL
# =============================================================================


@pytest.mark.unit
@pytest.mark.email
class TestGetEmailLogDetail:
    """Tests for get_email_log_detail() service method."""

    def test_returns_full_detail(self, db, email_logs):
        """Detail includes body content and all metadata."""
        service = EmailTemplateService(db)
        log = email_logs[0]
        detail = service.get_email_log_detail(log.id)

        assert detail["id"] == log.id
        assert detail["recipient_email"] == log.recipient_email
        assert detail["subject"] == log.subject
        assert detail["status"] == log.status
        assert detail["body_html"] is not None
        assert detail["body_text"] is not None
        assert detail["from_email"] == "noreply@orion.lu"

    def test_includes_timestamp_fields(self, db, email_logs):
        """Detail includes all timestamp fields."""
        service = EmailTemplateService(db)
        log = email_logs[0]
        detail = service.get_email_log_detail(log.id)

        assert "created_at" in detail
        assert "sent_at" in detail
        assert "delivered_at" in detail
        assert "opened_at" in detail
        assert "clicked_at" in detail

    def test_not_found_raises(self, db):
        """Non-existent log ID raises ResourceNotFoundException."""
        from app.exceptions.base import ResourceNotFoundException

        service = EmailTemplateService(db)

        with pytest.raises(ResourceNotFoundException):
            service.get_email_log_detail(999999)

    def test_includes_provider_info(self, db, email_logs):
        """Detail includes provider information."""
        service = EmailTemplateService(db)
        log = email_logs[0]
        detail = service.get_email_log_detail(log.id)

        assert detail["provider"] == "debug"


# =============================================================================
# GET EMAIL LOG STATS
# =============================================================================


@pytest.mark.unit
@pytest.mark.email
class TestGetEmailLogStats:
    """Tests for get_email_log_stats() service method."""

    def test_returns_status_counts(self, db, email_logs):
        """Stats include counts grouped by status."""
        service = EmailTemplateService(db)
        stats = service.get_email_log_stats()

        assert stats["by_status"]["sent"] == 3
        assert stats["by_status"]["failed"] == 2
        assert stats["by_status"]["pending"] == 1
        assert stats["by_status"]["delivered"] == 2
        assert stats["by_status"]["bounced"] == 1

    def test_returns_template_counts(self, db, multi_template_logs):
        """Stats include counts grouped by template_code."""
        service = EmailTemplateService(db)
        stats = service.get_email_log_stats()

        assert stats["by_template"]["signup_welcome"] == 2
        assert stats["by_template"]["order_confirmation"] == 2
        assert stats["by_template"]["password_reset"] == 3  # 2 sent + 1 failed

    def test_returns_total(self, db, email_logs):
        """Stats total matches sum of all statuses."""
        service = EmailTemplateService(db)
        stats = service.get_email_log_stats()

        assert stats["total"] == 9
        assert stats["total"] == sum(stats["by_status"].values())

    def test_empty_database(self, db):
        """Stats return zeros when no logs exist."""
        service = EmailTemplateService(db)
        stats = service.get_email_log_stats()

        assert stats["total"] == 0
        assert stats["by_status"] == {}
        assert stats["by_template"] == {}
