"""
SSH utilities for building consistent SSH options across the application
"""

import os
import logging
from typing import Dict

from .platform_utils import is_flatpak

logger = logging.getLogger(__name__)

# Markers shared by FM / SCP when classifying a failed ssh/scp run as auth.
# "permission denied" alone is NOT a marker: scp prints it for remote *file*
# permission errors ("scp: /path: Permission denied"). SSH auth failures use
# the parenthesized method list or the retry phrasing.
_SSH_AUTH_FAILURE_MARKERS = (
    'permission denied (',
    'permission denied, please try again',
    'authentication failed',
    'too many authentication failures',
)


def is_ssh_auth_failure_text(text: str) -> bool:
    """True when *text* looks like an SSH authentication failure."""
    lowered = (text or '').lower()
    return any(marker in lowered for marker in _SSH_AUTH_FAILURE_MARKERS)


def clean_ssh_stderr(text: str) -> str:
    """Drop ``ssh -v`` ``debug`` chatter, leaving the human-meaningful lines.

    With verbose logging on, ssh floods stderr with ``debugN:`` lines; showing
    that raw log in the UI is noise. Returns the stripped, joined remainder.
    """
    return "\n".join(
        line.strip()
        for line in (text or "").splitlines()
        if line.strip() and not line.lstrip().startswith("debug")
    ).strip()


def ensure_writable_ssh_home(env: Dict[str, str]) -> None:
    """Ensure ssh-copy-id has a writable HOME when running in Flatpak."""
    if is_flatpak():
        runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
        alt_home = os.path.join(runtime_dir, "sshcopyid-home")
        os.makedirs(os.path.join(alt_home, ".ssh"), exist_ok=True)
        env["HOME"] = alt_home
        logger.debug(f"Using temporary HOME for ssh-copy-id: {alt_home}")

def build_connection_ssh_options(connection, config=None, for_ssh_copy_id=False):
    """Build SSH options that match the exact connection settings used for SSH.
    
    This function replicates the SSH option building logic from terminal.py
    to ensure ssh-copy-id and scp use the same settings as SSH connections.
    
    Args:
        connection: The connection object
        config: Optional config object
        for_ssh_copy_id: If True, filter out options not supported by ssh-copy-id
    """
    options = []
    
    # Read SSH behavior from config with sane defaults (same as terminal.py)
    try:
        if config is None:
            from .config import Config
            config = Config()
        ssh_cfg = config.get_ssh_config() if hasattr(config, 'get_ssh_config') else {}
    except Exception:
        ssh_cfg = {}

    def _coerce_int(value, default=None):
        try:
            coerced = int(str(value))
            if coerced <= 0:
                return default
            return coerced
        except (TypeError, ValueError):
            return default

    connect_timeout = _coerce_int(ssh_cfg.get('connection_timeout'), None)
    connection_attempts = _coerce_int(ssh_cfg.get('connection_attempts'), None)
    keepalive_interval = _coerce_int(ssh_cfg.get('keepalive_interval'), None)
    keepalive_count = _coerce_int(ssh_cfg.get('keepalive_count_max'), None)
    strict_host = str(ssh_cfg.get('strict_host_key_checking', '') or '').strip()
    auto_add_host_keys = bool(ssh_cfg.get('auto_add_host_keys', True))
    batch_mode = bool(ssh_cfg.get('batch_mode', False))
    compression = bool(ssh_cfg.get('compression', False))

    # Determine auth method from connection and whether a password is available
    password_auth_selected = False
    has_saved_password = False
    try:
        # In our UI: 0 = key-based, 1 = password
        auth_method = getattr(connection, 'auth_method', 0)
        password_auth_selected = (auth_method == 1)
        has_saved_password = bool(getattr(connection, 'password', None))
    except Exception:
        password_auth_selected = False
        has_saved_password = False
    using_password = password_auth_selected or (not password_auth_selected and has_saved_password)

    # Apply advanced args according to stored preferences (same as terminal.py)
    # Only enable BatchMode when NOT doing password auth (BatchMode disables prompts)
    if batch_mode and not using_password:
        options.extend(['-o', 'BatchMode=yes'])
    if connect_timeout is not None:
        options.extend(['-o', f'ConnectTimeout={connect_timeout}'])
    if connection_attempts is not None:
        options.extend(['-o', f'ConnectionAttempts={connection_attempts}'])
    if for_ssh_copy_id:
        options.extend(['-o', 'NumberOfPasswordPrompts=1'])
    if keepalive_interval is not None:
        options.extend(['-o', f'ServerAliveInterval={keepalive_interval}'])
    if keepalive_count is not None:
        options.extend(['-o', f'ServerAliveCountMax={keepalive_count}'])
    if strict_host:
        options.extend(['-o', f'StrictHostKeyChecking={strict_host}'])
    if compression and not for_ssh_copy_id:
        options.append('-C')

    # Default to accepting new host keys non-interactively on fresh installs (same as terminal.py)
    try:
        if (not strict_host) and auto_add_host_keys:
            options.extend(['-o', 'StrictHostKeyChecking=accept-new'])
    except Exception:
        pass

    # Ensure SSH exits immediately on failure rather than waiting in background (same as terminal.py)
    options.extend(['-o', 'ExitOnForwardFailure=yes'])
    
    # Only add verbose flag if explicitly enabled in config (same as terminal.py)
    # Note: ssh-copy-id doesn't support -v flags, only -x for debug
    try:
        verbosity = int(ssh_cfg.get('verbosity', 0))
        debug_enabled = bool(ssh_cfg.get('debug_enabled', False))
        v = max(0, min(3, verbosity))
        if not for_ssh_copy_id:
            for _ in range(v):
                options.append('-v')
        # Map verbosity to LogLevel to ensure messages are not suppressed by defaults
        if v == 1:
            options.extend(['-o', 'LogLevel=VERBOSE'])
        elif v == 2:
            options.extend(['-o', 'LogLevel=DEBUG2'])
        elif v >= 3:
            options.extend(['-o', 'LogLevel=DEBUG3'])
        elif debug_enabled:
            options.extend(['-o', 'LogLevel=DEBUG'])
    except Exception as e:
        logger.warning(f"Could not check SSH verbosity/debug settings: {e}")
    
    # Add key file/options only for key-based auth (same as terminal.py)
    # Note: ssh-copy-id already specifies the key with -i, so we don't add it again
    if not password_auth_selected:
        # Get key selection mode
        key_select_mode = 0
        try:
            key_select_mode = int(getattr(connection, 'key_select_mode', 0) or 0)
        except Exception:
            pass
        
        # Only add specific key when a dedicated key mode is selected
        if key_select_mode in (1, 2) and hasattr(connection, 'keyfile') and connection.keyfile and \
           os.path.isfile(connection.keyfile) and \
           not connection.keyfile.startswith('Select key file'):

            if not for_ssh_copy_id:
                options.extend(['-i', connection.keyfile])
            if key_select_mode == 1:
                options.extend(['-o', 'IdentitiesOnly=yes'])
            
            # Add certificate if specified
            if hasattr(connection, 'certificate') and connection.certificate and \
               os.path.isfile(connection.certificate):
                options.extend(['-o', f'CertificateFile={connection.certificate}'])

            # If a password is available, allow all standard authentication methods
            if has_saved_password:
                options.extend([
                    '-o',
                    'PreferredAuthentications=gssapi-with-mic,hostbased,publickey,keyboard-interactive,password'
                ])
        else:
            # If no specific key or certificate, still append combined auth if password exists
            if has_saved_password:
                options.extend([
                    '-o',
                    'PreferredAuthentications=gssapi-with-mic,hostbased,publickey,keyboard-interactive,password'
                ])
    else:
        # Force password authentication when user chose password auth (same as terminal.py)
        # But don't disable pubkey auth for ssh-copy-id since we're installing a key
        options.extend([
            '-o',
            'PreferredAuthentications=keyboard-interactive,password',
        ])
        if not for_ssh_copy_id and getattr(connection, 'pubkey_auth_no', False):
            options.extend(['-o', 'PubkeyAuthentication=no'])
    
    # Add extra SSH config options from advanced tab (same as terminal.py)
    extra_ssh_config = getattr(connection, 'extra_ssh_config', '').strip()
    if extra_ssh_config:
        logger.debug(f"Adding extra SSH config options: {extra_ssh_config}")
        # Parse and add each extra SSH config option
        for line in extra_ssh_config.split('\n'):
            line = line.strip()
            if line and not line.startswith('#'):  # Skip empty lines and comments
                # Split on first space to separate option and value
                parts = line.split(' ', 1)
                if len(parts) == 2:
                    option, value = parts
                    options.extend(['-o', f"{option}={value}"])
                    logger.debug(f"Added SSH option: {option}={value}")
                elif len(parts) == 1:
                    # Option without value (e.g., "Compression yes" becomes "Compression=yes")
                    option = parts[0]
                    options.extend(['-o', f"{option}=yes"])
                    logger.debug(f"Added SSH option: {option}=yes")
    
    # Add X11 forwarding if enabled (same as terminal.py) - not supported by ssh-copy-id
    if hasattr(connection, 'x11_forwarding') and connection.x11_forwarding and not for_ssh_copy_id:
        options.append('-X')
    
    return options
