"""
Unit tests for platform/admin/mail_server.py

Tests MailServerService for managing email accounts.
"""
from unittest.mock import Mock, patch, MagicMock
import pytest


class TestMailServerErrorException:
    """Tests for MailServerError exception."""

    def test_exception_exists(self):
        """Should have MailServerError exception class."""
        from smoothschedule.platform.admin.mail_server import MailServerError

        assert issubclass(MailServerError, Exception)

    def test_can_raise_with_message(self):
        """Should be raisable with a message."""
        from smoothschedule.platform.admin.mail_server import MailServerError

        with pytest.raises(MailServerError) as exc_info:
            raise MailServerError("Connection failed")

        assert str(exc_info.value) == "Connection failed"


class TestMailServerServiceConstants:
    """Tests for MailServerService class constants."""

    def test_class_exists(self):
        """Should have MailServerService class."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        assert MailServerService is not None

    def test_has_ssh_host_constant(self):
        """Should have SSH_HOST constant."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        assert hasattr(MailServerService, 'SSH_HOST')
        assert isinstance(MailServerService.SSH_HOST, str)

    def test_has_ssh_user_constant(self):
        """Should have SSH_USER constant."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        assert hasattr(MailServerService, 'SSH_USER')
        assert isinstance(MailServerService.SSH_USER, str)

    def test_has_docker_container_constant(self):
        """Should have DOCKER_CONTAINER constant."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        assert hasattr(MailServerService, 'DOCKER_CONTAINER')
        assert isinstance(MailServerService.DOCKER_CONTAINER, str)

    def test_has_ssh_timeout_constant(self):
        """Should have SSH_TIMEOUT constant."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        assert hasattr(MailServerService, 'SSH_TIMEOUT')
        assert isinstance(MailServerService.SSH_TIMEOUT, int)


class TestMailServerServiceInit:
    """Tests for MailServerService initialization."""

    def test_init_uses_defaults(self):
        """Should use default constants when settings not provided."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()

        assert service.ssh_host == MailServerService.SSH_HOST
        assert service.ssh_user == MailServerService.SSH_USER
        assert service.docker_container == MailServerService.DOCKER_CONTAINER

    def test_init_reads_settings(self):
        """Should read settings if provided."""
        from smoothschedule.platform.admin.mail_server import MailServerService
        from django.conf import settings

        with patch.object(settings, 'MAIL_SERVER_SSH_HOST', 'custom.example.com', create=True):
            service = MailServerService()
            # The service reads from settings in __init__

        # Service should be properly initialized
        assert service is not None


class TestMailServerServiceMethods:
    """Tests for MailServerService methods."""

    def test_has_run_ssh_command_method(self):
        """Should have _run_ssh_command method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, '_run_ssh_command')
        assert callable(service._run_ssh_command)

    def test_has_create_account_method(self):
        """Should have create_account method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'create_account')
        assert callable(service.create_account)

    def test_has_delete_account_method(self):
        """Should have delete_account method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'delete_account')
        assert callable(service.delete_account)

    def test_has_update_password_method(self):
        """Should have update_password method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'update_password')
        assert callable(service.update_password)

    def test_has_list_accounts_method(self):
        """Should have list_accounts method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'list_accounts')
        assert callable(service.list_accounts)

    def test_has_account_exists_method(self):
        """Should have account_exists method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'account_exists')
        assert callable(service.account_exists)

    def test_has_test_connection_method(self):
        """Should have test_connection method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'test_connection')
        assert callable(service.test_connection)

    def test_has_sync_account_method(self):
        """Should have sync_account method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert hasattr(service, 'sync_account')
        assert callable(service.sync_account)


class TestMailServerServiceRunSshCommand:
    """Tests for _run_ssh_command method."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_run_ssh_command_success(self, mock_run):
        """Should return success tuple when command succeeds."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(
            returncode=0,
            stdout='Success output',
            stderr=''
        )

        service = MailServerService()
        success, stdout, stderr = service._run_ssh_command('echo test')

        assert success is True
        assert stdout == 'Success output'
        assert stderr == ''

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_run_ssh_command_failure(self, mock_run):
        """Should return failure tuple when command fails."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(
            returncode=1,
            stdout='',
            stderr='Error: command failed'
        )

        service = MailServerService()
        success, stdout, stderr = service._run_ssh_command('invalid command')

        assert success is False
        assert 'Error' in stderr


class TestMailServerServiceCreateAccount:
    """Tests for create_account method."""

    def test_create_account_method_exists(self):
        """Should have create_account method with correct signature."""
        from smoothschedule.platform.admin.mail_server import MailServerService
        import inspect

        service = MailServerService()
        sig = inspect.signature(service.create_account)
        params = list(sig.parameters.keys())

        assert 'email' in params
        assert 'password' in params


class TestMailServerServiceDeleteAccount:
    """Tests for delete_account method."""

    def test_delete_account_method_exists(self):
        """Should have delete_account method with correct signature."""
        from smoothschedule.platform.admin.mail_server import MailServerService
        import inspect

        service = MailServerService()
        sig = inspect.signature(service.delete_account)
        params = list(sig.parameters.keys())

        assert 'email' in params


class TestMailServerServiceListAccounts:
    """Tests for list_accounts method."""

    def test_list_accounts_method_signature(self):
        """Should have list_accounts method."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        assert callable(service.list_accounts)


class TestMailServerServiceAccountExists:
    """Tests for account_exists method."""

    def test_account_exists_method_signature(self):
        """Should have account_exists method with email parameter."""
        from smoothschedule.platform.admin.mail_server import MailServerService
        import inspect

        service = MailServerService()
        sig = inspect.signature(service.account_exists)
        params = list(sig.parameters.keys())

        assert 'email' in params


class TestGetMailServerService:
    """Tests for get_mail_server_service helper function."""

    def test_function_exists(self):
        """Should have get_mail_server_service function."""
        from smoothschedule.platform.admin.mail_server import get_mail_server_service

        assert callable(get_mail_server_service)

    def test_returns_mail_server_service(self):
        """Should return MailServerService instance."""
        from smoothschedule.platform.admin.mail_server import get_mail_server_service, MailServerService

        service = get_mail_server_service()

        assert isinstance(service, MailServerService)


class TestMailServerServiceCreateAccountExecution:
    """Tests for create_account method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_create_account_returns_tuple_on_success(self, mock_run):
        """Should return (True, message) when account creation succeeds."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # First call for account_exists (list accounts)
        # Second call for create_account
        mock_run.return_value = Mock(returncode=0, stdout='', stderr='')

        service = MailServerService()
        success, message = service.create_account('new@example.com', 'secure123')

        assert success is True
        assert 'Successfully' in message

    def test_create_account_rejects_invalid_email(self):
        """Should return False for invalid email format."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        service = MailServerService()
        success, message = service.create_account('invalid-email', 'password')

        assert success is False
        assert 'Invalid' in message


class TestMailServerServiceDeleteAccountExecution:
    """Tests for delete_account method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_delete_account_returns_tuple_on_success(self, mock_run):
        """Should return (True, message) when deletion succeeds."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # First call returns list with the account (account_exists check)
        # Second call does the delete
        mock_run.side_effect = [
            Mock(returncode=0, stdout='* test@example.com ( 0 / 0 )', stderr=''),
            Mock(returncode=0, stdout='Account deleted', stderr='')
        ]

        service = MailServerService()
        success, message = service.delete_account('test@example.com')

        assert success is True
        assert 'Successfully' in message

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_delete_account_returns_false_if_not_exists(self, mock_run):
        """Should return False when account doesn't exist."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # Account doesn't exist
        mock_run.return_value = Mock(returncode=0, stdout='', stderr='')

        service = MailServerService()
        success, message = service.delete_account('nonexistent@example.com')

        assert success is False
        assert 'does not exist' in message


class TestMailServerServiceListAccountsExecution:
    """Tests for list_accounts method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_list_accounts_returns_list(self, mock_run):
        """Should return list of accounts."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(
            returncode=0,
            stdout='* user1@example.com ( 0 / 0 )\n* user2@example.com ( 0 / 0 )',
            stderr=''
        )

        service = MailServerService()
        result = service.list_accounts()

        assert isinstance(result, list)
        assert len(result) == 2

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_list_accounts_raises_on_failure(self, mock_run):
        """Should raise MailServerError when query fails."""
        from smoothschedule.platform.admin.mail_server import MailServerService, MailServerError

        mock_run.return_value = Mock(returncode=1, stdout='', stderr='Connection failed')

        service = MailServerService()

        with pytest.raises(MailServerError):
            service.list_accounts()


class TestMailServerServiceAccountExistsExecution:
    """Tests for account_exists method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_account_exists_returns_true_for_existing(self, mock_run):
        """Should return True for existing accounts."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(
            returncode=0,
            stdout='* existing@example.com ( 0 / 0 )',
            stderr=''
        )

        service = MailServerService()
        result = service.account_exists('existing@example.com')

        assert result is True

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_account_exists_returns_false_for_nonexistent(self, mock_run):
        """Should return False for non-existing accounts."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # Empty list
        mock_run.return_value = Mock(returncode=0, stdout='', stderr='')

        service = MailServerService()
        result = service.account_exists('nonexistent@example.com')

        assert result is False

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_account_exists_returns_false_on_error(self, mock_run):
        """Should return False when list_accounts fails."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(returncode=1, stdout='', stderr='Error')

        service = MailServerService()
        result = service.account_exists('any@example.com')

        assert result is False


class TestMailServerServiceTestConnectionExecution:
    """Tests for test_connection method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_test_connection_returns_tuple_on_success(self, mock_run):
        """Should return (True, message) when connection succeeds."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(returncode=0, stdout='Connection successful', stderr='')

        service = MailServerService()
        success, message = service.test_connection()

        assert success is True
        assert 'Successfully' in message

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_test_connection_returns_false_on_failure(self, mock_run):
        """Should return (False, message) when connection fails."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(returncode=1, stdout='', stderr='Connection refused')

        service = MailServerService()
        success, message = service.test_connection()

        assert success is False
        assert 'Failed' in message


class TestMailServerServiceUpdatePasswordExecution:
    """Tests for update_password method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_update_password_returns_tuple_on_success(self, mock_run):
        """Should return (True, message) when password update succeeds."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # First call for account_exists, second for update
        mock_run.side_effect = [
            Mock(returncode=0, stdout='* user@example.com ( 0 / 0 )', stderr=''),
            Mock(returncode=0, stdout='Password updated', stderr='')
        ]

        service = MailServerService()
        success, message = service.update_password('user@example.com', 'newpassword')

        assert success is True
        assert 'Successfully' in message

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_update_password_returns_false_if_not_exists(self, mock_run):
        """Should return False when account doesn't exist."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(returncode=0, stdout='', stderr='')

        service = MailServerService()
        success, message = service.update_password('nonexistent@example.com', 'newpass')

        assert success is False
        assert 'does not exist' in message


class TestMailServerServiceSyncAccountExecution:
    """Tests for sync_account method execution."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_sync_account_creates_new_account(self, mock_run):
        """Should create account if it doesn't exist."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # Empty list (account doesn't exist) then success
        mock_run.side_effect = [
            Mock(returncode=0, stdout='', stderr=''),  # account_exists
            Mock(returncode=0, stdout='', stderr=''),  # account_exists in create
            Mock(returncode=0, stdout='Created', stderr='')  # create
        ]

        mock_platform_email = Mock()
        mock_platform_email.email_address = 'new@example.com'
        mock_platform_email.password = 'password123'

        service = MailServerService()
        success, message = service.sync_account(mock_platform_email)

        assert success is True
        mock_platform_email.save.assert_called()

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_sync_account_updates_existing_account(self, mock_run):
        """Should update password if account exists."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        # Account exists then update succeeds
        mock_run.side_effect = [
            Mock(returncode=0, stdout='* existing@example.com ( 0 / 0 )', stderr=''),  # account_exists
            Mock(returncode=0, stdout='* existing@example.com ( 0 / 0 )', stderr=''),  # account_exists in update
            Mock(returncode=0, stdout='Updated', stderr='')  # update
        ]

        mock_platform_email = Mock()
        mock_platform_email.email_address = 'existing@example.com'
        mock_platform_email.password = 'newpassword'

        service = MailServerService()
        success, message = service.sync_account(mock_platform_email)

        assert success is True
        mock_platform_email.save.assert_called()


class TestMailServerServiceDeleteAndUnsync:
    """Tests for delete_and_unsync method."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_delete_and_unsync_deletes_existing(self, mock_run):
        """Should delete account if it exists."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.side_effect = [
            Mock(returncode=0, stdout='* test@example.com ( 0 / 0 )', stderr=''),
            Mock(returncode=0, stdout='* test@example.com ( 0 / 0 )', stderr=''),
            Mock(returncode=0, stdout='Deleted', stderr='')
        ]

        mock_platform_email = Mock()
        mock_platform_email.email_address = 'test@example.com'

        service = MailServerService()
        success, message = service.delete_and_unsync(mock_platform_email)

        assert success is True

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_delete_and_unsync_returns_true_if_not_exists(self, mock_run):
        """Should return True if account not on server."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.return_value = Mock(returncode=0, stdout='', stderr='')

        mock_platform_email = Mock()
        mock_platform_email.email_address = 'notexist@example.com'

        service = MailServerService()
        success, message = service.delete_and_unsync(mock_platform_email)

        assert success is True
        assert 'was not on mail server' in message


class TestMailServerServiceSshCommandException:
    """Tests for _run_ssh_command exception handling."""

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_handles_timeout_exception(self, mock_run):
        """Should handle subprocess timeout gracefully."""
        import subprocess
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.side_effect = subprocess.TimeoutExpired('ssh', 30)

        service = MailServerService()
        success, stdout, stderr = service._run_ssh_command('slow command')

        assert success is False
        assert 'timeout' in stderr.lower() or 'timed out' in stderr.lower()

    @patch('smoothschedule.platform.admin.mail_server.subprocess.run')
    def test_handles_general_exception(self, mock_run):
        """Should handle general exceptions gracefully."""
        from smoothschedule.platform.admin.mail_server import MailServerService

        mock_run.side_effect = Exception('SSH connection failed')

        service = MailServerService()
        success, stdout, stderr = service._run_ssh_command('command')

        assert success is False
        assert 'failed' in stderr.lower() or 'error' in stderr.lower()
