"""
Mail Server Service for managing email accounts on mail.talova.net.

This service manages email accounts via SSH commands to the docker-mailserver
container running on mail.talova.net.

Commands used:
- docker exec mailserver setup email add user@domain password
- docker exec mailserver setup email update user@domain password
- docker exec mailserver setup email del user@domain
- docker exec mailserver setup email list
"""

import logging
import subprocess
import shlex
from typing import Optional
from django.conf import settings
from django.utils import timezone

logger = logging.getLogger(__name__)


class MailServerError(Exception):
    """Exception raised when mail server operations fail."""
    pass


class MailServerService:
    """
    Service for managing email accounts on the mail.talova.net server.

    Uses SSH to execute docker commands on the remote mail server.
    """

    # Mail server configuration
    SSH_HOST = 'mail.talova.net'
    SSH_USER = 'poduck'
    DOCKER_CONTAINER = 'mailserver'
    DOCKER_COMPOSE_PATH = '~/docker-mailserver'

    # SSH key configuration (persistent keys mounted in container)
    SSH_KEY_PATH = '/app/.ssh/id_ed25519'
    SSH_KNOWN_HOSTS_PATH = '/app/.ssh/known_hosts'

    # SSH connection timeout (seconds)
    SSH_TIMEOUT = 30

    def __init__(self):
        self.ssh_host = getattr(settings, 'MAIL_SERVER_SSH_HOST', self.SSH_HOST)
        self.ssh_user = getattr(settings, 'MAIL_SERVER_SSH_USER', self.SSH_USER)
        self.docker_container = getattr(settings, 'MAIL_SERVER_DOCKER_CONTAINER', self.DOCKER_CONTAINER)
        self.ssh_key_path = getattr(settings, 'MAIL_SERVER_SSH_KEY_PATH', self.SSH_KEY_PATH)
        self.ssh_known_hosts_path = getattr(settings, 'MAIL_SERVER_SSH_KNOWN_HOSTS_PATH', self.SSH_KNOWN_HOSTS_PATH)

    def _run_ssh_command(self, command: str, timeout: Optional[int] = None) -> tuple[bool, str, str]:
        """
        Execute a command on the mail server via SSH.

        Args:
            command: The command to execute on the remote server
            timeout: Optional timeout in seconds

        Returns:
            Tuple of (success, stdout, stderr)
        """
        timeout = timeout or self.SSH_TIMEOUT

        ssh_command = [
            'ssh',
            '-i', self.ssh_key_path,
            '-o', 'StrictHostKeyChecking=accept-new',
            '-o', f'UserKnownHostsFile={self.ssh_known_hosts_path}',
            '-o', 'ConnectTimeout=10',
            '-o', 'BatchMode=yes',
            f'{self.ssh_user}@{self.ssh_host}',
            command
        ]

        logger.info(f"Executing SSH command: {' '.join(ssh_command[:6])} [command hidden]")

        try:
            result = subprocess.run(
                ssh_command,
                capture_output=True,
                text=True,
                timeout=timeout
            )

            success = result.returncode == 0
            stdout = result.stdout.strip()
            stderr = result.stderr.strip()

            if not success:
                logger.warning(f"SSH command failed: {stderr}")
            else:
                logger.debug(f"SSH command succeeded: {stdout[:100]}...")

            return success, stdout, stderr

        except subprocess.TimeoutExpired:
            logger.error(f"SSH command timed out after {timeout}s")
            return False, '', f'Command timed out after {timeout} seconds'

        except Exception as e:
            logger.error(f"SSH command error: {str(e)}")
            return False, '', str(e)

    def _run_docker_command(self, docker_args: str) -> tuple[bool, str, str]:
        """
        Execute a docker command on the mail server.

        Args:
            docker_args: Arguments to pass to docker exec

        Returns:
            Tuple of (success, stdout, stderr)
        """
        command = f"docker exec {self.docker_container} {docker_args}"
        return self._run_ssh_command(command)

    def list_accounts(self) -> list[dict]:
        """
        List all email accounts on the mail server.

        Returns:
            List of account dictionaries with email and quota info
        """
        success, stdout, stderr = self._run_docker_command('setup email list')

        if not success:
            raise MailServerError(f"Failed to list accounts: {stderr}")

        accounts = []
        for line in stdout.split('\n'):
            line = line.strip()
            if not line or line.startswith('*'):
                # Parse format: "* user@domain ( size / quota ) [percent%]"
                if line.startswith('*'):
                    parts = line.split()
                    if len(parts) >= 2:
                        email = parts[1]
                        accounts.append({
                            'email': email,
                            'raw_line': line
                        })

        return accounts

    def account_exists(self, email: str) -> bool:
        """
        Check if an email account exists on the mail server.

        Args:
            email: The email address to check

        Returns:
            True if the account exists, False otherwise
        """
        try:
            accounts = self.list_accounts()
            return any(acc['email'].lower() == email.lower() for acc in accounts)
        except MailServerError:
            return False

    def create_account(self, email: str, password: str) -> tuple[bool, str]:
        """
        Create a new email account on the mail server.

        Args:
            email: The email address to create
            password: The password for the account

        Returns:
            Tuple of (success, message)
        """
        # Validate email format
        if '@' not in email:
            return False, 'Invalid email format'

        # Check if account already exists
        if self.account_exists(email):
            return False, f'Account {email} already exists'

        # Escape password for shell (use single quotes and escape any single quotes in password)
        escaped_password = password.replace("'", "'\"'\"'")

        # Create the account
        command = f"setup email add {shlex.quote(email)} '{escaped_password}'"
        success, stdout, stderr = self._run_docker_command(command)

        if success:
            logger.info(f"Created email account: {email}")
            return True, f'Successfully created account {email}'
        else:
            logger.error(f"Failed to create account {email}: {stderr}")
            return False, f'Failed to create account: {stderr}'

    def update_password(self, email: str, new_password: str) -> tuple[bool, str]:
        """
        Update the password for an existing email account.

        Args:
            email: The email address to update
            new_password: The new password

        Returns:
            Tuple of (success, message)
        """
        # Check if account exists
        if not self.account_exists(email):
            return False, f'Account {email} does not exist'

        # Escape password for shell
        escaped_password = new_password.replace("'", "'\"'\"'")

        # Update the password
        command = f"setup email update {shlex.quote(email)} '{escaped_password}'"
        success, stdout, stderr = self._run_docker_command(command)

        if success:
            logger.info(f"Updated password for: {email}")
            return True, f'Successfully updated password for {email}'
        else:
            logger.error(f"Failed to update password for {email}: {stderr}")
            return False, f'Failed to update password: {stderr}'

    def delete_account(self, email: str) -> tuple[bool, str]:
        """
        Delete an email account from the mail server.

        Args:
            email: The email address to delete

        Returns:
            Tuple of (success, message)
        """
        # Check if account exists
        if not self.account_exists(email):
            return False, f'Account {email} does not exist'

        # Delete the account (use -y to confirm deletion)
        command = f"setup email del -y {shlex.quote(email)}"
        success, stdout, stderr = self._run_docker_command(command)

        if success:
            logger.info(f"Deleted email account: {email}")
            return True, f'Successfully deleted account {email}'
        else:
            logger.error(f"Failed to delete account {email}: {stderr}")
            return False, f'Failed to delete account: {stderr}'

    def test_connection(self) -> tuple[bool, str]:
        """
        Test SSH connection to the mail server.

        Returns:
            Tuple of (success, message)
        """
        success, stdout, stderr = self._run_ssh_command('echo "Connection successful"')

        if success and 'Connection successful' in stdout:
            return True, 'Successfully connected to mail server'
        else:
            return False, f'Failed to connect: {stderr or "Unknown error"}'

    def sync_account(self, platform_email) -> tuple[bool, str]:
        """
        Sync a PlatformEmailAddress to the mail server.

        Creates the account if it doesn't exist, or updates the password if it does.

        Args:
            platform_email: PlatformEmailAddress instance

        Returns:
            Tuple of (success, message)
        """
        email = platform_email.email_address
        password = platform_email.password

        try:
            if self.account_exists(email):
                # Update existing account password
                success, message = self.update_password(email, password)
            else:
                # Create new account
                success, message = self.create_account(email, password)

            # Update sync status
            platform_email.mail_server_synced = success
            platform_email.last_synced_at = timezone.now()
            if not success:
                platform_email.last_sync_error = message
            else:
                platform_email.last_sync_error = ''
            platform_email.save(update_fields=[
                'mail_server_synced',
                'last_synced_at',
                'last_sync_error'
            ])

            return success, message

        except MailServerError as e:
            platform_email.mail_server_synced = False
            platform_email.last_sync_error = str(e)
            platform_email.last_synced_at = timezone.now()
            platform_email.save(update_fields=[
                'mail_server_synced',
                'last_synced_at',
                'last_sync_error'
            ])
            return False, str(e)

    def delete_and_unsync(self, platform_email) -> tuple[bool, str]:
        """
        Delete a PlatformEmailAddress from the mail server.

        Args:
            platform_email: PlatformEmailAddress instance

        Returns:
            Tuple of (success, message)
        """
        email = platform_email.email_address

        try:
            if self.account_exists(email):
                success, message = self.delete_account(email)
                return success, message
            else:
                # Account doesn't exist on server, that's fine
                return True, f'Account {email} was not on mail server'

        except MailServerError as e:
            return False, str(e)


# Singleton instance
_mail_server_service = None


def get_mail_server_service() -> MailServerService:
    """Get the mail server service singleton instance."""
    global _mail_server_service
    if _mail_server_service is None:
        _mail_server_service = MailServerService()
    return _mail_server_service
