from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from app.services.emailer import EmailService


class TestEmailService:
    """Test email service (Mailgun-based)"""

    @pytest.fixture
    def service(self):
        return EmailService()

    @pytest.fixture
    def sample_download_links(self):
        return {
            "en": {
                "captions_vtt": "https://signed-url.example.com/en/captions.vtt",
                "audio_description_vtt": "https://signed-url.example.com/en/ad.vtt",
                "audio_description_mp3": "https://signed-url.example.com/en/ad.mp3",
            },
            "es": {
                "captions_vtt": "https://signed-url.example.com/es/captions.vtt",
                "audio_description_vtt": "https://signed-url.example.com/es/ad.vtt",
                "audio_description_mp3": "https://signed-url.example.com/es/ad.mp3",
            },
        }

    # ── _configured property ───────────────────────────────────────────────────

    def test_configured_when_keys_present(self, service):
        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = "key"
            s.mailgun_domain = "mg.example.com"
            assert service._configured is True

    def test_not_configured_when_key_missing(self, service):
        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = ""
            s.mailgun_domain = "mg.example.com"
            assert service._configured is False

    def test_not_configured_when_domain_missing(self, service):
        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = "key"
            s.mailgun_domain = ""
            assert service._configured is False

    # ── _send ─────────────────────────────────────────────────────────────────

    @pytest.mark.asyncio
    async def test_send_returns_false_when_not_configured(self, service):
        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = ""
            s.mailgun_domain = ""
            result = await service._send("to@example.com", "Subject", "<p>HTML</p>")
        assert result is False

    @pytest.mark.asyncio
    async def test_send_success(self, service):
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = AsyncMock()
        mock_client.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client.__aexit__ = AsyncMock(return_value=False)
        mock_client.post = AsyncMock(return_value=mock_response)

        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = "key"
            s.mailgun_domain = "mg.example.com"
            s.mailgun_from = "noreply@example.com"
            with patch("httpx.AsyncClient", return_value=mock_client):
                result = await service._send("to@example.com", "Subject", "<p>Hi</p>")

        assert result is True

    @pytest.mark.asyncio
    async def test_send_api_failure(self, service):
        mock_response = MagicMock()
        mock_response.status_code = 400
        mock_response.text = "Bad request"

        mock_client = AsyncMock()
        mock_client.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client.__aexit__ = AsyncMock(return_value=False)
        mock_client.post = AsyncMock(return_value=mock_response)

        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = "key"
            s.mailgun_domain = "mg.example.com"
            s.mailgun_from = "noreply@example.com"
            with patch("httpx.AsyncClient", return_value=mock_client):
                result = await service._send("to@example.com", "Subject", "<p>Hi</p>")

        assert result is False

    @pytest.mark.asyncio
    async def test_send_exception_returns_false(self, service):
        with patch("app.services.emailer.settings") as s:
            s.mailgun_api_key = "key"
            s.mailgun_domain = "mg.example.com"
            s.mailgun_from = "noreply@example.com"
            with patch("httpx.AsyncClient", side_effect=Exception("network error")):
                result = await service._send("to@example.com", "Subject", "<p>Hi</p>")

        assert result is False

    # ── send_completion_email ─────────────────────────────────────────────────

    @pytest.mark.asyncio
    async def test_send_completion_email_calls_send(self, service, sample_download_links):
        with patch.object(service, "_send", new_callable=AsyncMock) as mock_send:
            mock_send.return_value = True
            result = await service.send_completion_email(
                recipient_email="client@example.com",
                job_title="Test Video",
                download_links=sample_download_links,
            )
        assert result is True
        mock_send.assert_called_once()
        # Subject should contain job title
        assert "Test Video" in mock_send.call_args[0][1]

    # ── _render_completion_template ───────────────────────────────────────────

    def test_render_template_basic(self, service, sample_download_links):
        html = service._render_completion_template(
            job_title="Test Video Project",
            download_links=sample_download_links,
        )
        assert "Test Video Project" in html
        assert "EN Assets" in html
        assert "ES Assets" in html
        assert "24 hours" in html

    def test_render_template_single_language(self, service):
        html = service._render_completion_template(
            job_title="English Only",
            download_links={"en": {"captions_vtt": "https://example.com/cap.vtt"}},
        )
        assert "English Only" in html
        assert "EN Assets" in html
        assert "ES Assets" not in html

    def test_render_template_no_downloads(self, service):
        html = service._render_completion_template(job_title="Empty Job", download_links={})
        assert "Empty Job" in html
        assert "<!DOCTYPE html>" in html
        assert "24 hours" in html

    def test_render_template_html_structure(self, service, sample_download_links):
        html = service._render_completion_template(
            job_title="Test", download_links=sample_download_links
        )
        assert html.startswith("<!DOCTYPE html>") or "<!DOCTYPE html>" in html
        assert "<html>" in html
        assert "</html>" in html
        assert "<body>" in html
        assert "font-family: Arial" in html

    def test_render_template_download_links(self, service):
        html = service._render_completion_template(
            job_title="Test",
            download_links={"en": {
                "captions_vtt": "https://example.com/captions.vtt",
                "audio_description_mp3": "https://example.com/ad.mp3",
            }},
        )
        assert "Download Captions Vtt" in html
        assert "Download Audio Description Mp3" in html
        assert 'href="https://example.com/captions.vtt"' in html
        assert 'href="https://example.com/ad.mp3"' in html

    def test_render_template_xss_escaping(self, service):
        html = service._render_completion_template(
            job_title="<script>alert('xss')</script>Title",
            download_links={},
        )
        assert "<script>" not in html
        assert "Title" in html
