"""Tests for amsdal_mail backends."""

import os
from io import StringIO

import pytest

from amsdal_mail.backends import BACKENDS
from amsdal_mail.backends import get_connection
from amsdal_mail.backends import import_string
from amsdal_mail.backends.base import BaseEmailBackend
from amsdal_mail.backends.console import ConsoleBackend
from amsdal_mail.backends.dummy import DummyBackend
from amsdal_mail.message import Attachment
from amsdal_mail.message import EmailMessage


class TestImportString:
    """Tests for import_string utility."""

    def test_import_string_valid_path(self):
        """Test importing with valid dotted path."""
        cls = import_string('amsdal_mail.backends.console.ConsoleBackend')
        assert cls == ConsoleBackend

    def test_import_string_invalid_path(self):
        """Test importing with invalid path raises ImportError."""
        with pytest.raises(ImportError, match="doesn't look like a module path"):
            import_string('invalid')

    def test_import_string_nonexistent_module(self):
        """Test importing nonexistent module raises ImportError."""
        with pytest.raises(ImportError, match='could not be imported'):
            import_string('nonexistent.module.Class')

    def test_import_string_nonexistent_class(self):
        """Test importing nonexistent class raises ImportError."""
        with pytest.raises(ImportError, match='does not define'):
            import_string('amsdal_mail.backends.console.NonexistentClass')


class TestGetConnection:
    """Tests for get_connection function."""

    def test_get_connection_with_registered_backend(self):
        """Test getting connection with registered backend name."""
        conn = get_connection('console')
        assert isinstance(conn, ConsoleBackend)

    def test_get_connection_with_full_path(self):
        """Test getting connection with full dotted path."""
        conn = get_connection('amsdal_mail.backends.dummy.DummyBackend')
        assert isinstance(conn, DummyBackend)

    def test_get_connection_default_backend(self):
        """Test default backend is console."""
        conn = get_connection()
        assert isinstance(conn, ConsoleBackend)

    def test_get_connection_with_env_var(self, monkeypatch):
        """Test getting connection from environment variable."""
        monkeypatch.setenv('AMSDAL_EMAIL_BACKEND', 'dummy')
        conn = get_connection()
        assert isinstance(conn, DummyBackend)

    def test_get_connection_with_kwargs(self):
        """Test passing kwargs to backend."""
        conn = get_connection('dummy', store_messages=True)
        assert isinstance(conn, DummyBackend)
        assert conn.store_messages is True

    def test_get_connection_with_fail_silently(self):
        """Test fail_silently parameter."""
        conn = get_connection('console', fail_silently=True)
        assert conn.fail_silently is True


class TestBackendRegistry:
    """Tests for BACKENDS registry."""

    def test_backends_contains_expected_entries(self):
        """Test BACKENDS registry contains expected backends."""
        assert 'console' in BACKENDS
        assert 'dummy' in BACKENDS
        assert 'smtp' in BACKENDS

    def test_backends_values_are_dotted_paths(self):
        """Test BACKENDS values are dotted paths."""
        for name, path in BACKENDS.items():
            assert '.' in path
            assert path.startswith('amsdal_mail.backends.')


class TestConsoleBackend:
    """Tests for ConsoleBackend."""

    def test_console_backend_default_stream(self):
        """Test ConsoleBackend uses stdout by default."""
        import sys

        backend = ConsoleBackend()
        assert backend.stream == sys.stdout

    def test_console_backend_custom_stream(self):
        """Test ConsoleBackend with custom stream."""
        stream = StringIO()
        backend = ConsoleBackend(stream=stream)
        assert backend.stream == stream

    def test_console_backend_send_simple_message(self):
        """Test sending a simple message outputs to stream."""
        stream = StringIO()
        backend = ConsoleBackend(stream=stream)

        msg = EmailMessage(
            subject='Test Subject',
            body='Test body',
            from_email='sender@example.com',
            to=['recipient@example.com'],
        )

        status = backend.send_messages([msg])

        assert status.is_success

        assert len(status.recipients) == 1
        output = stream.getvalue()
        assert 'Test Subject' in output
        assert 'Test body' in output
        assert 'sender@example.com' in output
        assert 'recipient@example.com' in output

    def test_console_backend_send_multiple_messages(self):
        """Test sending multiple messages."""
        stream = StringIO()
        backend = ConsoleBackend(stream=stream)

        messages = [
            EmailMessage(
                subject=f'Message {i}',
                body=f'Body {i}',
                from_email='sender@example.com',
                to=['recipient@example.com'],
            )
            for i in range(3)
        ]

        status = backend.send_messages(messages)

        assert status.is_success
        # Same recipient in all 3 messages, so only 1 unique recipient
        assert len(status.recipients) == 1
        output = stream.getvalue()
        assert 'Message 0' in output
        assert 'Message 1' in output
        assert 'Message 2' in output

    def test_console_backend_with_html_body(self):
        """Test console backend displays HTML body."""
        stream = StringIO()
        backend = ConsoleBackend(stream=stream)

        msg = EmailMessage(
            subject='HTML Test',
            body='Plain text',
            html_body='<h1>HTML</h1>',
            from_email='sender@example.com',
            to=['recipient@example.com'],
        )

        backend.send_messages([msg])
        output = stream.getvalue()

        assert 'Plain Text' in output
        assert 'HTML' in output
        assert '<h1>HTML</h1>' in output

    def test_console_backend_with_attachments(self):
        """Test console backend displays attachment info."""
        stream = StringIO()
        backend = ConsoleBackend(stream=stream)

        attachment = Attachment(
            filename='test.pdf',
            content=b'PDF content here',
            mimetype='application/pdf',
        )

        msg = EmailMessage(
            subject='With Attachment',
            body='See attachment',
            from_email='sender@example.com',
            to=['recipient@example.com'],
            attachments=[attachment],
        )

        backend.send_messages([msg])
        output = stream.getvalue()

        assert 'Attachments' in output
        assert 'test.pdf' in output
        assert 'application/pdf' in output

    def test_console_backend_context_manager(self):
        """Test using ConsoleBackend as context manager."""
        stream = StringIO()

        with ConsoleBackend(stream=stream) as backend:
            msg = EmailMessage(
                subject='Test',
                body='Body',
                from_email='sender@example.com',
                to=['recipient@example.com'],
            )
            backend.send_messages([msg])

        assert 'Test' in stream.getvalue()


class TestDummyBackend:
    """Tests for DummyBackend."""

    def test_dummy_backend_send_messages_returns_count(self):
        """Test DummyBackend returns correct status."""
        backend = DummyBackend()

        messages = [
            EmailMessage(
                subject='Test',
                body='Body',
                from_email='sender@example.com',
                to=['recipient@example.com'],
            )
            for _ in range(5)
        ]

        status = backend.send_messages(messages)
        assert status.is_success
        # Same recipient in all 5 messages, so only 1 unique recipient
        assert len(status.recipients) == 1

    def test_dummy_backend_without_storage(self):
        """Test DummyBackend doesn't store messages by default."""
        backend = DummyBackend()

        msg = EmailMessage(
            subject='Test',
            body='Body',
            from_email='sender@example.com',
            to=['recipient@example.com'],
        )

        backend.send_messages([msg])
        assert len(backend.messages) == 0

    def test_dummy_backend_with_storage(self):
        """Test DummyBackend stores messages when enabled."""
        backend = DummyBackend(store_messages=True)

        msg = EmailMessage(
            subject='Test',
            body='Body',
            from_email='sender@example.com',
            to=['recipient@example.com'],
        )

        backend.send_messages([msg])
        assert len(backend.messages) == 1
        assert backend.messages[0].subject == 'Test'

    def test_dummy_backend_stores_multiple_messages(self):
        """Test DummyBackend stores multiple messages."""
        backend = DummyBackend(store_messages=True)

        messages = [
            EmailMessage(
                subject=f'Test {i}',
                body='Body',
                from_email='sender@example.com',
                to=['recipient@example.com'],
            )
            for i in range(3)
        ]

        backend.send_messages(messages)
        assert len(backend.messages) == 3
        assert backend.messages[0].subject == 'Test 0'
        assert backend.messages[2].subject == 'Test 2'

    def test_dummy_backend_context_manager(self):
        """Test using DummyBackend as context manager."""
        backend = DummyBackend(store_messages=True)
        with backend:
            msg = EmailMessage(
                subject='Test',
                body='Body',
                from_email='sender@example.com',
                to=['recipient@example.com'],
            )
            backend.send_messages([msg])
            assert len(backend.messages) == 1


class TestBaseEmailBackend:
    """Tests for BaseEmailBackend."""

    def test_base_backend_cannot_be_instantiated(self):
        """Test BaseEmailBackend is abstract and cannot be instantiated directly."""
        with pytest.raises(TypeError):
            BaseEmailBackend()  # type: ignore[abstract]

    def test_base_backend_send_messages_not_implemented(self):
        """Test send_messages raises NotImplementedError."""

        class TestBackend(BaseEmailBackend):
            """Incomplete backend for testing."""

            pass

        with pytest.raises(TypeError):
            TestBackend()  # type: ignore[abstract]
