from unittest.mock import MagicMock, patch

import pytest

from app.services.emailer import EmailService


class TestEmailService:
    """Test email service functionality"""

    @pytest.fixture
    def email_service(self):
        """Create email service with mocked SendGrid client"""
        with patch('app.services.emailer.settings') as mock_settings:
            mock_settings.sendgrid_api_key = "test_api_key"
            mock_settings.email_from = "support@example.com"
            
            with patch('app.services.emailer.SendGridAPIClient') as mock_client:
                service = EmailService()
                service.client = MagicMock()
                return service

    @pytest.fixture
    def sample_download_links(self):
        """Sample download links for testing"""
        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"
            }
        }

    @pytest.mark.asyncio
    async def test_send_completion_email_success(self, email_service, sample_download_links):
        """Test successful completion email sending"""
        # Mock successful SendGrid response
        mock_response = MagicMock()
        mock_response.status_code = 202
        email_service.client.send.return_value = mock_response
        
        result = await email_service.send_completion_email(
            recipient_email="client@example.com",
            job_title="Test Video Project",
            download_links=sample_download_links
        )
        
        assert result is True
        email_service.client.send.assert_called_once()

    @pytest.mark.asyncio
    async def test_send_completion_email_no_client(self):
        """Test email sending when client is not configured"""
        service = EmailService()
        service.client = None
        
        result = await service.send_completion_email(
            recipient_email="client@example.com",
            job_title="Test Video",
            download_links={}
        )
        
        assert result is False

    @pytest.mark.asyncio
    async def test_send_completion_email_api_failure(self, email_service, sample_download_links):
        """Test email sending with SendGrid API failure"""
        # Mock failed SendGrid response
        mock_response = MagicMock()
        mock_response.status_code = 400
        email_service.client.send.return_value = mock_response
        
        result = await email_service.send_completion_email(
            recipient_email="client@example.com",
            job_title="Test Video",
            download_links=sample_download_links
        )
        
        assert result is False

    @pytest.mark.asyncio
    async def test_send_completion_email_exception(self, email_service, sample_download_links):
        """Test email sending with exception"""
        # Mock SendGrid client raising exception
        email_service.client.send.side_effect = Exception("SendGrid error")
        
        result = await email_service.send_completion_email(
            recipient_email="client@example.com",
            job_title="Test Video",
            download_links=sample_download_links
        )
        
        assert result is False

    def test_render_completion_template_basic(self, email_service, sample_download_links):
        """Test rendering completion email template"""
        html_content = email_service._render_completion_template(
            job_title="Test Video Project",
            download_links=sample_download_links
        )
        
        # Check that key elements are present
        assert "Test Video Project" in html_content
        assert "EN Assets" in html_content
        assert "ES Assets" in html_content
        assert "captions.vtt" in html_content
        assert "audio_description_vtt" in html_content
        assert "audio_description_mp3" in html_content
        assert "24 hours" in html_content  # Expiry warning

    def test_render_completion_template_single_language(self, email_service):
        """Test rendering template with single language"""
        download_links = {
            "en": {
                "captions_vtt": "https://example.com/captions.vtt"
            }
        }
        
        html_content = email_service._render_completion_template(
            job_title="English Only Video",
            download_links=download_links
        )
        
        assert "English Only Video" in html_content
        assert "EN Assets" in html_content
        assert "ES Assets" not in html_content

    def test_render_completion_template_no_downloads(self, email_service):
        """Test rendering template with no download links"""
        html_content = email_service._render_completion_template(
            job_title="Empty Job",
            download_links={}
        )
        
        assert "Empty Job" in html_content
        assert "<!DOCTYPE html>" in html_content
        assert "24 hours" in html_content

    def test_render_completion_template_html_structure(self, email_service, sample_download_links):
        """Test that rendered template has proper HTML structure"""
        html_content = email_service._render_completion_template(
            job_title="Test Video",
            download_links=sample_download_links
        )
        
        # Check HTML structure
        assert html_content.startswith("<!DOCTYPE html>")
        assert "<html>" in html_content
        assert "</html>" in html_content
        assert "<head>" in html_content
        assert "<body>" in html_content
        assert "font-family: Arial" in html_content  # CSS present

    def test_render_completion_template_download_link_formatting(self, email_service):
        """Test that download links are properly formatted in template"""
        download_links = {
            "en": {
                "captions_vtt": "https://example.com/captions.vtt",
                "audio_description_mp3": "https://example.com/ad.mp3"
            }
        }
        
        html_content = email_service._render_completion_template(
            job_title="Test Video",
            download_links=download_links
        )
        
        # Check that file types are properly formatted
        assert "Download Captions Vtt" in html_content
        assert "Download Audio Description Mp3" in html_content
        assert 'href="https://example.com/captions.vtt"' in html_content
        assert 'href="https://example.com/ad.mp3"' in html_content

    def test_service_initialization_with_api_key(self):
        """Test service initialization with SendGrid API key"""
        with patch('app.services.emailer.settings') as mock_settings:
            mock_settings.sendgrid_api_key = "test_api_key"
            
            with patch('app.services.emailer.SendGridAPIClient') as mock_client:
                service = EmailService()
                
                mock_client.assert_called_once_with(api_key="test_api_key")
                assert service.client is not None

    def test_service_initialization_without_api_key(self):
        """Test service initialization without SendGrid API key"""
        with patch('app.services.emailer.settings') as mock_settings:
            mock_settings.sendgrid_api_key = ""
            
            service = EmailService()
            
            assert service.client is None

    @pytest.mark.asyncio
    async def test_send_completion_email_mail_object_creation(self, email_service, sample_download_links):
        """Test that Mail object is created correctly"""
        mock_response = MagicMock()
        mock_response.status_code = 202
        email_service.client.send.return_value = mock_response
        
        with patch('app.services.emailer.Mail') as mock_mail:
            mock_mail_instance = MagicMock()
            mock_mail.return_value = mock_mail_instance
            
            await email_service.send_completion_email(
                recipient_email="client@example.com",
                job_title="Test Video",
                download_links=sample_download_links
            )
            
            # Verify Mail object was created with correct parameters
            mock_mail.assert_called_once()
            call_args = mock_mail.call_args
            
            # Check that from_email, to_emails, subject, and html_content are set
            assert call_args is not None
            email_service.client.send.assert_called_once_with(mock_mail_instance)

    def test_template_injection_safety(self, email_service):
        """Test that template is safe from injection attacks"""
        malicious_title = "<script>alert('xss')</script>Malicious Title"
        malicious_links = {
            "en": {
                "captions_vtt": "javascript:alert('xss')"
            }
        }
        
        html_content = email_service._render_completion_template(
            job_title=malicious_title,
            download_links=malicious_links
        )
        
        # Jinja2 should escape HTML by default
        assert "<script>" not in html_content
        assert "javascript:" in html_content  # URL would still be there but not executed
        assert "Malicious Title" in html_content