# Tests for NAVIG OS Adapters
"""
Test suite for OS adapter classes (Windows, Linux, macOS).
"""

import os
import sys
import platform
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.adapters.os import get_os_adapter, detect_os, OSAdapter
from navig.adapters.os.base import PackageInfo, SecurityCheck
from navig.adapters.os.factory import get_os_adapter_for_remote


class TestOSDetection:
    """Tests for OS detection functions."""
    
    def test_detect_os_returns_valid_os(self):
        """Test that detect_os returns a valid OS name."""
        os_name = detect_os()
        assert os_name in ['windows', 'linux', 'macos']
    
    def test_detect_os_matches_platform(self):
        """Test that detect_os matches the actual platform."""
        os_name = detect_os()
        
        if sys.platform == 'win32':
            assert os_name == 'windows'
        elif sys.platform == 'darwin':
            assert os_name == 'macos'
        else:
            assert os_name == 'linux'
    
    def test_get_os_adapter_returns_adapter(self):
        """Test that get_os_adapter returns an OSAdapter instance."""
        adapter = get_os_adapter()
        assert isinstance(adapter, OSAdapter)
    
    def test_get_os_adapter_with_os_name(self):
        """Test getting adapter for specific OS."""
        for os_name in ['windows', 'linux', 'macos']:
            adapter = get_os_adapter(os_name)
            assert isinstance(adapter, OSAdapter)
            assert adapter.name == os_name
    
    def test_get_os_adapter_invalid_os(self):
        """Test that invalid OS name raises error."""
        with pytest.raises(ValueError):
            get_os_adapter('invalid_os')


class TestPackageInfo:
    """Tests for PackageInfo dataclass."""
    
    def test_package_info_creation(self):
        """Test creating a PackageInfo."""
        pkg = PackageInfo(
            name='python',
            version='3.11.0',
            description='Python interpreter',
            source='apt'
        )
        assert pkg.name == 'python'
        assert pkg.version == '3.11.0'
        assert pkg.description == 'Python interpreter'
        assert pkg.source == 'apt'
    
    def test_package_info_to_dict(self):
        """Test PackageInfo to_dict method."""
        pkg = PackageInfo(name='git', version='2.40.0', source='brew')
        d = pkg.to_dict()
        
        assert d['name'] == 'git'
        assert d['version'] == '2.40.0'
        assert d['source'] == 'brew'


class TestSecurityCheck:
    """Tests for SecurityCheck dataclass."""
    
    def test_security_check_creation(self):
        """Test creating a SecurityCheck."""
        check = SecurityCheck(
            category='firewall',
            status='ok',
            message='Firewall is active'
        )
        assert check.category == 'firewall'
        assert check.status == 'ok'
        assert check.message == 'Firewall is active'
    
    def test_security_check_with_details(self):
        """Test SecurityCheck with details."""
        check = SecurityCheck(
            category='ports',
            status='warning',
            message='Many open ports',
            details={'count': 50}
        )
        assert check.details == {'count': 50}
    
    def test_security_check_to_dict(self):
        """Test SecurityCheck to_dict method."""
        check = SecurityCheck(
            category='users',
            status='critical',
            message='Root login enabled'
        )
        d = check.to_dict()
        
        assert d['category'] == 'users'
        assert d['status'] == 'critical'


class TestOSAdapterInterface:
    """Tests for OSAdapter interface compliance."""
    
    @pytest.fixture
    def adapter(self):
        """Get an adapter for the current OS."""
        return get_os_adapter()
    
    def test_adapter_has_name(self, adapter):
        """Test adapter has name property."""
        assert hasattr(adapter, 'name')
        assert adapter.name in ['windows', 'linux', 'macos']
    
    def test_adapter_has_display_name(self, adapter):
        """Test adapter has display_name property."""
        assert hasattr(adapter, 'display_name')
        assert isinstance(adapter.display_name, str)
        assert len(adapter.display_name) > 0
    
    def test_get_package_list_command(self, adapter):
        """Test get_package_list_command returns a string."""
        cmd = adapter.get_package_list_command()
        assert isinstance(cmd, str)
        assert len(cmd) > 0
    
    def test_get_hosts_file_path(self, adapter):
        """Test get_hosts_file_path returns a Path."""
        path = adapter.get_hosts_file_path()
        assert isinstance(path, Path)
        # Should contain 'hosts' in the path
        assert 'hosts' in str(path).lower()
    
    def test_get_home_directory(self, adapter):
        """Test get_home_directory returns a valid path."""
        path = adapter.get_home_directory()
        assert isinstance(path, Path)
        # Home directory should exist
        assert path.exists()
    
    def test_get_temp_directory(self, adapter):
        """Test get_temp_directory returns a valid path."""
        path = adapter.get_temp_directory()
        assert isinstance(path, Path)
        # Temp directory should exist
        assert path.exists()
    
    def test_check_admin_privileges(self, adapter):
        """Test check_admin_privileges returns boolean."""
        result = adapter.check_admin_privileges()
        assert isinstance(result, bool)
    
    def test_get_firewall_status_command(self, adapter):
        """Test firewall status command."""
        cmd = adapter.get_firewall_status_command()
        assert isinstance(cmd, str)
    
    def test_get_open_ports_command(self, adapter):
        """Test open ports command."""
        cmd = adapter.get_open_ports_command()
        assert isinstance(cmd, str)
    
    def test_default_shell(self, adapter):
        """Test default_shell property."""
        shell = adapter.default_shell
        assert isinstance(shell, str)
        assert len(shell) > 0
    
    def test_path_separator(self, adapter):
        """Test path_separator property."""
        sep = adapter.path_separator
        assert sep in ['/', '\\']
    
    def test_line_ending(self, adapter):
        """Test line_ending property."""
        ending = adapter.line_ending
        assert ending in ['\n', '\r\n']
    
    def test_get_ping_command(self, adapter):
        """Test ping command generation."""
        cmd = adapter.get_ping_command('google.com', count=2)
        assert isinstance(cmd, str)
        assert 'google.com' in cmd
        assert '2' in cmd  # count should be in command


class TestWindowsAdapter:
    """Windows-specific adapter tests."""
    
    @pytest.fixture
    def adapter(self):
        """Get Windows adapter."""
        return get_os_adapter('windows')
    
    def test_windows_hosts_path(self, adapter):
        """Test Windows hosts file path."""
        path = adapter.get_hosts_file_path()
        assert 'System32' in str(path) or 'system32' in str(path)
        assert 'hosts' in str(path)
    
    def test_windows_package_command(self, adapter):
        """Test Windows package list uses winget."""
        cmd = adapter.get_package_list_command()
        assert 'winget' in cmd.lower()
    
    def test_windows_path_separator(self, adapter):
        """Test Windows path separator."""
        assert adapter.path_separator == '\\'
    
    def test_windows_line_ending(self, adapter):
        """Test Windows line ending."""
        assert adapter.line_ending == '\r\n'
    
    def test_parse_winget_output(self, adapter):
        """Test parsing winget list output."""
        sample_output = """Name                            Id                           Version
----------------------------------------------------------------------------------------------------------
Python 3.11.0                   Python.Python.3.11           3.11.0
Git                             Git.Git                      2.40.1
Visual Studio Code              Microsoft.VisualStudioCode   1.85.0
"""
        packages = adapter.parse_package_list(sample_output)
        # Should parse some packages
        assert len(packages) > 0


class TestLinuxAdapter:
    """Linux-specific adapter tests."""
    
    @pytest.fixture
    def adapter(self):
        """Get Linux adapter."""
        return get_os_adapter('linux')
    
    def test_linux_hosts_path(self, adapter):
        """Test Linux hosts file path."""
        path = adapter.get_hosts_file_path()
        # Compare parts to handle cross-platform Path behavior
        assert path.parts[-2:] == ('etc', 'hosts')
    
    def test_linux_path_separator(self, adapter):
        """Test Linux path separator."""
        assert adapter.path_separator == '/'
    
    def test_linux_line_ending(self, adapter):
        """Test Linux line ending."""
        assert adapter.line_ending == '\n'
    
    def test_linux_package_manager_detection(self, adapter):
        """Test package manager detection."""
        pm = adapter.package_manager
        assert pm in ['apt', 'dnf', 'yum', 'pacman', 'zypper', 'apk']
    
    def test_parse_dpkg_output(self, adapter):
        """Test parsing dpkg output."""
        sample_output = """python3\t3.11.2\tPython interpreter
git\t2.39.2\tVersion control system
curl\t7.88.1\tCommand line tool for transferring data
"""
        packages = adapter.parse_package_list(sample_output)
        assert len(packages) == 3
        assert packages[0].name == 'python3'
        assert packages[0].version == '3.11.2'


class TestMacOSAdapter:
    """macOS-specific adapter tests."""
    
    @pytest.fixture
    def adapter(self):
        """Get macOS adapter."""
        return get_os_adapter('macos')
    
    def test_macos_hosts_path(self, adapter):
        """Test macOS hosts file path."""
        path = adapter.get_hosts_file_path()
        # Compare parts to handle cross-platform Path behavior
        assert path.parts[-2:] == ('etc', 'hosts')
    
    def test_macos_package_command(self, adapter):
        """Test macOS package list uses brew."""
        cmd = adapter.get_package_list_command()
        assert 'brew' in cmd
    
    def test_macos_path_separator(self, adapter):
        """Test macOS path separator."""
        assert adapter.path_separator == '/'
    
    def test_macos_line_ending(self, adapter):
        """Test macOS line ending."""
        assert adapter.line_ending == '\n'
    
    def test_macos_default_shell(self, adapter):
        """Test macOS default shell (usually zsh)."""
        shell = adapter.default_shell
        # Should be zsh or bash
        assert 'sh' in shell
    
    def test_parse_brew_output(self, adapter):
        """Test parsing brew list output."""
        sample_output = """python@3.11 3.11.7
git 2.43.0
node 21.5.0
"""
        packages = adapter.parse_package_list(sample_output)
        assert len(packages) == 3
        assert packages[0].name == 'python@3.11'
        assert packages[0].version == '3.11.7'


class TestGetOSAdapterForRemote:
    """Tests for remote OS detection."""
    
    def test_detect_windows_from_uname(self):
        """Test detecting Windows from remote info."""
        adapter = get_os_adapter_for_remote('MINGW64_NT-10.0')
        assert adapter.name == 'windows'
    
    def test_detect_linux_from_uname(self):
        """Test detecting Linux from remote info."""
        adapter = get_os_adapter_for_remote('Linux')
        assert adapter.name == 'linux'
    
    def test_detect_macos_from_uname(self):
        """Test detecting macOS from remote info."""
        adapter = get_os_adapter_for_remote('Darwin')
        assert adapter.name == 'macos'
    
    def test_default_to_linux(self):
        """Test that unknown OS defaults to Linux."""
        adapter = get_os_adapter_for_remote('FreeBSD')
        assert adapter.name == 'linux'


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