"""
Tests for email functionality
"""

import pytest

pytestmark = [pytest.mark.unit, pytest.mark.utils]

from unittest.mock import patch, MagicMock
from flask import current_app
from app.utils.email import send_email, check_email_configuration, send_test_email, init_mail


class TestEmailConfiguration:
    """Tests for email configuration"""

    def test_init_mail(self, app):
        """Test email initialization"""
        with app.app_context():
            mail = init_mail(app)
            assert mail is not None
            assert "MAIL_SERVER" in app.config
            assert "MAIL_PORT" in app.config
            assert "MAIL_DEFAULT_SENDER" in app.config

    def test_email_config_status_not_configured(self, app):
        """Test email configuration status when not configured"""
        with app.app_context():
            # Reset mail server to simulate unconfigured state
            app.config["MAIL_SERVER"] = "localhost"

            status = check_email_configuration()

            assert status is not None
            assert "configured" in status
            assert "settings" in status
            assert "errors" in status
            assert "warnings" in status
            assert status["configured"] is False
            assert len(status["errors"]) > 0

    def test_email_config_status_configured(self, app):
        """Test email configuration status when properly configured"""
        with app.app_context():
            # Set up proper configuration
            app.config["MAIL_SERVER"] = "smtp.gmail.com"
            app.config["MAIL_PORT"] = 587
            app.config["MAIL_USE_TLS"] = True
            app.config["MAIL_USE_SSL"] = False
            app.config["MAIL_USERNAME"] = "test@example.com"
            app.config["MAIL_PASSWORD"] = "test_password"
            app.config["MAIL_DEFAULT_SENDER"] = "noreply@example.com"

            status = check_email_configuration()

            assert status is not None
            assert status["configured"] is True
            assert len(status["errors"]) == 0
            assert status["settings"]["server"] == "smtp.gmail.com"
            assert status["settings"]["port"] == 587
            assert status["settings"]["password_set"] is True

    def test_email_config_warns_about_default_sender(self, app):
        """Test that configuration warns about default sender"""
        with app.app_context():
            app.config["MAIL_SERVER"] = "smtp.gmail.com"
            app.config["MAIL_DEFAULT_SENDER"] = "noreply@timetracker.local"

            status = check_email_configuration()

            assert len(status["warnings"]) > 0
            assert any("Default sender" in w for w in status["warnings"])

    def test_email_config_errors_on_both_tls_and_ssl(self, app):
        """Test that configuration errors when both TLS and SSL are enabled"""
        with app.app_context():
            app.config["MAIL_SERVER"] = "smtp.gmail.com"
            app.config["MAIL_USE_TLS"] = True
            app.config["MAIL_USE_SSL"] = True

            status = check_email_configuration()

            assert len(status["errors"]) > 0
            assert any("TLS and SSL" in e for e in status["errors"])


class TestSendEmail:
    """Tests for sending emails"""

    @patch("app.utils.email.mail.send")
    @patch("app.utils.email.Thread")
    def test_send_email_success(self, mock_thread, mock_send, app):
        """Test sending email successfully"""
        with app.app_context():
            app.config["MAIL_SERVER"] = "smtp.gmail.com"

            send_email(
                subject="Test Subject",
                recipients=["test@example.com"],
                text_body="Test body",
                html_body="<p>Test body</p>",
            )

            # Verify thread was started for async sending
            assert mock_thread.called

    def test_send_email_no_server(self, app):
        """Test sending email with no mail server configured"""
        with app.app_context():
            app.config["MAIL_SERVER"] = None

            # This should not raise an exception, but log a warning and return early
            send_email(subject="Test Subject", recipients=["test@example.com"], text_body="Test body")

            # The function should return without error
            # (The warning is logged but we can't easily capture it in this context)

    def test_send_email_no_recipients(self, app):
        """Test sending email with no recipients"""
        with app.app_context():
            app.config["MAIL_SERVER"] = "smtp.gmail.com"

            # This should not raise an exception, but log a warning and return early
            send_email(subject="Test Subject", recipients=[], text_body="Test body")

            # The function should return without error
            # (The warning is logged but we can't easily capture it in this context)

    @patch("app.utils.email.mail.send")
    def test_send_test_email_success(self, mock_send, app):
        """Test sending test email successfully"""
        with app.app_context():
            app.config["MAIL_SERVER"] = "smtp.gmail.com"
            app.config["MAIL_DEFAULT_SENDER"] = "test@example.com"

            success, message = send_test_email("recipient@example.com", "Test Sender")

            assert success is True
            assert "successfully" in message.lower()
            assert mock_send.called

    def test_send_test_email_invalid_recipient(self, app):
        """Test sending test email with invalid recipient"""
        with app.app_context():
            success, message = send_test_email("invalid-email", "Test Sender")

            assert success is False
            assert "Invalid" in message

    def test_send_test_email_no_server(self, app):
        """Test sending test email with no mail server"""
        with app.app_context():
            app.config["MAIL_SERVER"] = None

            success, message = send_test_email("test@example.com", "Test Sender")

            assert success is False
            assert "not configured" in message

    @patch("app.utils.email.mail.send")
    def test_send_test_email_exception(self, mock_send, app):
        """Test sending test email with exception"""
        with app.app_context():
            app.config["MAIL_SERVER"] = "smtp.gmail.com"
            app.config["MAIL_DEFAULT_SENDER"] = "test@example.com"

            # Simulate exception
            mock_send.side_effect = Exception("SMTP error")

            success, message = send_test_email("test@example.com", "Test Sender")

            assert success is False
            assert "Failed" in message


class TestEmailIntegration:
    """Integration tests for email functionality"""

    def check_email_configuration_in_app_context(self, app):
        """Test that email configuration is available in app context"""
        with app.app_context():
            assert hasattr(current_app, "config")
            assert "MAIL_SERVER" in current_app.config
            assert "MAIL_PORT" in current_app.config
            assert "MAIL_USE_TLS" in current_app.config
            assert "MAIL_DEFAULT_SENDER" in current_app.config

    def test_email_settings_from_environment(self, app, monkeypatch):
        """Test that email settings are loaded from environment"""
        # Set environment variables
        monkeypatch.setenv("MAIL_SERVER", "smtp.test.com")
        monkeypatch.setenv("MAIL_PORT", "465")
        monkeypatch.setenv("MAIL_USE_SSL", "true")

        # Reinitialize mail with new environment
        with app.app_context():
            mail = init_mail(app)

            assert app.config["MAIL_SERVER"] == "smtp.test.com"
            assert app.config["MAIL_PORT"] == 465
            assert app.config["MAIL_USE_SSL"] is True


class TestDatabaseEmailConfiguration:
    """Tests for database-backed email configuration"""

    def test_get_mail_config_when_disabled(self, app):
        """Test get_mail_config returns None when database config is disabled"""
        with app.app_context():
            from app.models import Settings

            settings = Settings.get_settings()
            settings.mail_enabled = False
            settings.mail_server = "smtp.test.com"

            config = settings.get_mail_config()
            assert config is None

    def test_get_mail_config_when_enabled(self, app):
        """Test get_mail_config returns config when enabled"""
        with app.app_context():
            from app.models import Settings

            settings = Settings.get_settings()
            settings.mail_enabled = True
            settings.mail_server = "smtp.test.com"
            settings.mail_port = 587
            settings.mail_use_tls = True
            settings.mail_use_ssl = False
            settings.mail_username = "test@example.com"
            settings.mail_password = "test_password"
            settings.mail_default_sender = "noreply@example.com"

            config = settings.get_mail_config()

            assert config is not None
            assert config["MAIL_SERVER"] == "smtp.test.com"
            assert config["MAIL_PORT"] == 587
            assert config["MAIL_USE_TLS"] is True
            assert config["MAIL_USE_SSL"] is False
            assert config["MAIL_USERNAME"] == "test@example.com"
            assert config["MAIL_PASSWORD"] == "test_password"
            assert config["MAIL_DEFAULT_SENDER"] == "noreply@example.com"

    def test_init_mail_uses_database_config(self, app):
        """Test that init_mail uses database settings when available"""
        with app.app_context():
            from app.models import Settings
            from app.utils.email import init_mail
            from app import db

            settings = Settings.get_settings()
            settings.mail_enabled = True
            settings.mail_server = "smtp.database.com"
            settings.mail_port = 465
            db.session.commit()

            init_mail(app)

            # Should use database settings
            assert app.config["MAIL_SERVER"] == "smtp.database.com"
            assert app.config["MAIL_PORT"] == 465

    def test_reload_mail_config(self, app):
        """Test reloading email configuration"""
        with app.app_context():
            from app.models import Settings
            from app.utils.email import reload_mail_config
            from app import db

            # Set up database config
            settings = Settings.get_settings()
            settings.mail_enabled = True
            settings.mail_server = "smtp.reloaded.com"
            db.session.commit()

            # Reload configuration
            success = reload_mail_config(app)

            assert success is True
            assert app.config["MAIL_SERVER"] == "smtp.reloaded.com"

    def test_check_email_configuration_shows_source(self, app):
        """Test that configuration status shows source (database or environment)"""
        with app.app_context():
            from app.models import Settings
            from app.utils.email import check_email_configuration
            from app import db

            # Test with database config
            settings = Settings.get_settings()
            settings.mail_enabled = True
            settings.mail_server = "smtp.database.com"
            db.session.commit()

            status = check_email_configuration()

            assert "source" in status
            assert status["source"] == "database"

            # Test with environment config
            settings.mail_enabled = False
            db.session.commit()

            status = check_email_configuration()
            assert status["source"] == "environment"


# Fixtures
@pytest.fixture
def mock_mail_send():
    """Mock the mail.send method"""
    with patch("app.utils.email.mail.send") as mock:
        yield mock
