# Tests for NAVIG Connection Adapters
"""
Test suite for ConnectionAdapter classes (LocalConnection, SSHConnection).
"""

import os
import sys
import pytest
from pathlib import Path
from unittest.mock import patch, MagicMock

# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))

from navig.core.connection import (
    CommandResult,
    ConnectionAdapter,
    LocalConnection,
    SSHConnection,
    get_connection
)


class TestCommandResult:
    """Tests for CommandResult dataclass."""
    
    def test_command_result_creation(self):
        """Test creating a CommandResult."""
        result = CommandResult(
            stdout="hello world",
            stderr="",
            exit_code=0,
            duration=1.5
        )
        assert result.stdout == "hello world"
        assert result.stderr == ""
        assert result.exit_code == 0
        assert result.duration == 1.5
    
    def test_command_result_to_dict(self):
        """Test CommandResult to_dict method."""
        result = CommandResult(
            stdout="output",
            stderr="error",
            exit_code=1,
            duration=2.0
        )
        d = result.to_dict()
        assert d['stdout'] == "output"
        assert d['stderr'] == "error"
        assert d['exit_code'] == 1
        assert d['duration'] == 2.0
    
    def test_command_result_success_property(self):
        """Test success property of CommandResult."""
        success_result = CommandResult("", "", 0, 0.1)
        assert success_result.success is True
        
        fail_result = CommandResult("", "", 1, 0.1)
        assert fail_result.success is False


class TestLocalConnection:
    """Tests for LocalConnection class."""
    
    def test_local_connection_creation(self):
        """Test creating a LocalConnection."""
        conn = LocalConnection()
        # LocalConnection doesn't have is_connected - it's always connected
        assert conn is not None
    
    def test_local_connection_with_os_type(self):
        """Test LocalConnection with explicit OS type."""
        conn = LocalConnection(os_type='linux')
        assert conn._os_type == 'linux'
    
    def test_run_simple_command(self):
        """Test running a simple command."""
        conn = LocalConnection()
        
        # Test echo command (works on all platforms)
        if sys.platform == 'win32':
            result = conn.run('echo hello')
        else:
            result = conn.run('echo hello')
        
        assert result.exit_code == 0
        assert 'hello' in result.stdout.lower()
        assert result.duration >= 0
    
    def test_run_command_with_timeout(self):
        """Test command timeout handling."""
        conn = LocalConnection()
        
        # Very short timeout should work for fast commands
        result = conn.run('echo test', timeout=30.0)
        assert result.exit_code == 0
    
    def test_run_failing_command(self):
        """Test running a command that fails."""
        conn = LocalConnection()
        
        # Command that should fail on any platform
        if sys.platform == 'win32':
            result = conn.run('exit 1', timeout=5.0)
        else:
            result = conn.run('exit 1', timeout=5.0)
        
        assert result.exit_code == 1
    
    def test_detect_os(self):
        """Test OS detection."""
        conn = LocalConnection()
        detected_os = conn.detect_os()
        
        assert detected_os in ['windows', 'linux', 'macos', 'darwin']
        
        # Verify it matches platform
        if sys.platform == 'win32':
            assert detected_os == 'windows'
        elif sys.platform == 'darwin':
            assert detected_os in ['macos', 'darwin']
        else:
            assert detected_os == 'linux'
    
    def test_close(self):
        """Test closing connection."""
        conn = LocalConnection()
        conn.close()
        # Should not raise any errors
        assert True
    
    def test_upload_creates_copy(self, tmp_path):
        """Test that upload copies file locally."""
        conn = LocalConnection()
        
        # Create source file
        src = tmp_path / "source.txt"
        src.write_text("test content")
        
        dest = tmp_path / "dest.txt"
        
        result = conn.upload(src, dest)
        
        # Should succeed and create the dest file
        assert result is True
        assert dest.exists()
        assert dest.read_text() == "test content"
    
    def test_download_creates_copy(self, tmp_path):
        """Test that download copies file locally."""
        conn = LocalConnection()
        
        # Create source file
        src = tmp_path / "source.txt"
        src.write_text("test content")
        
        dest = tmp_path / "dest.txt"
        
        result = conn.download(src, dest)
        
        # Should succeed and create the dest file
        assert result is True
        assert dest.exists()


class TestSSHConnection:
    """Tests for SSHConnection class."""
    
    def test_ssh_connection_creation(self):
        """Test creating an SSHConnection."""
        config = {
            'host': 'example.com',
            'user': 'testuser',
            'port': 22
        }
        conn = SSHConnection(config)
        assert conn is not None
    
    def test_ssh_connection_stores_config(self):
        """Test SSHConnection stores config properly."""
        config = {
            'host': 'server.example.com',
            'user': 'admin',
            'port': 2222,
            'ssh_key': '/home/user/.ssh/id_rsa'
        }
        conn = SSHConnection(config)
        assert conn.host_config == config
    
    def test_ssh_run_executes_command(self):
        """Test that run attempts to execute via SSH (will fail without actual SSH)."""
        config = {
            'host': 'localhost',
            'user': 'testuser',
            'port': 22
        }
        conn = SSHConnection(config)
        
        # This will fail because no SSH server, but should return a result
        result = conn.run('echo hello', timeout=2.0)
        assert isinstance(result, CommandResult)
        # Will have non-zero exit code because SSH fails
        assert result.exit_code != 0


class TestGetConnection:
    """Tests for get_connection factory function."""
    
    def test_get_connection_local(self):
        """Test getting a local connection."""
        config = {'type': 'local'}
        conn = get_connection(config)
        
        assert isinstance(conn, LocalConnection)
    
    def test_get_connection_ssh(self):
        """Test getting an SSH connection."""
        config = {
            'type': 'ssh',
            'host': 'server.example.com',
            'user': 'admin'
        }
        conn = get_connection(config)
        
        assert isinstance(conn, SSHConnection)
        assert conn.host_config['host'] == 'server.example.com'
    
    def test_get_connection_ssh_with_all_options(self):
        """Test getting SSH connection with all options."""
        config = {
            'type': 'ssh',
            'host': 'server.example.com',
            'user': 'admin',
            'port': 2222,
            'ssh_key': '/path/to/key'
        }
        conn = get_connection(config)
        
        assert isinstance(conn, SSHConnection)
        assert conn.host_config['port'] == 2222
        assert conn.host_config['ssh_key'] == '/path/to/key'
    
    def test_get_connection_default_to_ssh(self):
        """Test that unspecified type defaults to SSH."""
        config = {
            'host': 'server.example.com',
            'user': 'admin'
        }
        conn = get_connection(config)
        
        # Should default to SSH for configs with hostname
        assert isinstance(conn, SSHConnection)


class TestConnectionAdapterInterface:
    """Tests for ConnectionAdapter interface compliance."""
    
    def test_local_connection_implements_interface(self):
        """Verify LocalConnection implements all required methods."""
        conn = LocalConnection()
        
        # Check all abstract methods exist
        assert hasattr(conn, 'run')
        assert hasattr(conn, 'upload')
        assert hasattr(conn, 'download')
        assert hasattr(conn, 'detect_os')
        assert hasattr(conn, 'close')
    
    def test_ssh_connection_implements_interface(self):
        """Verify SSHConnection implements all required methods."""
        config = {'host': 'example.com', 'user': 'user'}
        conn = SSHConnection(config)
        
        # Check all abstract methods exist
        assert hasattr(conn, 'run')
        assert hasattr(conn, 'upload')
        assert hasattr(conn, 'download')
        assert hasattr(conn, 'detect_os')
        assert hasattr(conn, 'close')


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
