"""
Tests for ConfigManager with new two-tier hierarchy.
"""

import pytest
import yaml
from pathlib import Path
import tempfile
import shutil
import os

from navig.config import ConfigManager


@pytest.fixture
def temp_home(monkeypatch):
    """Create temporary home directory for tests."""
    temp = tempfile.mkdtemp()
    monkeypatch.setenv('HOME', temp)
    monkeypatch.setenv('USERPROFILE', temp)  # Windows
    yield Path(temp)
    shutil.rmtree(temp)


@pytest.fixture
def config_manager(temp_home):
    """Create ConfigManager with temporary home, isolated from project's .navig."""
    # Pass explicit config_dir to skip auto-detection from cwd
    # which would find the project's actual .navig directory
    config_dir = temp_home / ".navig"
    return ConfigManager(config_dir=config_dir)


@pytest.fixture
def sample_host_config():
    """Sample host configuration (new format)."""
    return {
        'name': 'myhost',
        'host': 'srv.example.host',
        'port': 22,
        'user': 'root',
        'ssh_key': '~/.ssh/myhost',
        'default_app': 'myapp',
        'apps': {
            'myapp': {
                'database': {
                    'type': 'mysql',
                    'name': 'myapp_db'
                },
                'webserver': {
                    'type': 'apache2'
                }
            },
            'myapp-staging': {
                'database': {
                    'type': 'mysql',
                    'name': 'myapp_staging'
                },
                'webserver': {
                    'type': 'apache2'
                }
            }
        }
    }


@pytest.fixture
def sample_legacy_config():
    """Sample legacy configuration (old format)."""
    return {
        'name': 'production',
        'host': 'srv.example.com',
        'port': 22,
        'user': 'root',
        'ssh_key': '~/.ssh/production',
        'database': {
            'type': 'mysql',
            'name': 'myapp_db'
        },
        'services': {
            'web': 'nginx'
        }
    }


class TestHostManagement:
    """Test host management methods."""
    
    def test_host_exists_new_format(self, config_manager, sample_host_config):
        """Test host_exists with new format."""
        config_manager.save_host_config('myhost', sample_host_config)
        assert config_manager.host_exists('myhost')
        assert not config_manager.host_exists('nonexistent')
    
    def test_host_exists_legacy_format(self, config_manager, sample_legacy_config):
        """Test host_exists with legacy format."""
        # Save to legacy directory
        legacy_file = config_manager.apps_dir / 'production.yaml'
        with open(legacy_file, 'w') as f:
            yaml.dump(sample_legacy_config, f)
        
        assert config_manager.host_exists('production')
    
    def test_list_hosts_new_format(self, config_manager, sample_host_config):
        """Test list_hosts with new format."""
        config_manager.save_host_config('myhost', sample_host_config)
        config_manager.save_host_config('vps', sample_host_config)
        
        hosts = config_manager.list_hosts()
        assert 'myhost' in hosts
        assert 'vps' in hosts
        assert len(hosts) == 2
    
    def test_list_hosts_mixed_formats(self, config_manager, sample_host_config, sample_legacy_config):
        """Test list_hosts with both new and legacy formats."""
        # New format
        config_manager.save_host_config('myhost', sample_host_config)
        
        # Legacy format
        legacy_file = config_manager.apps_dir / 'production.yaml'
        with open(legacy_file, 'w') as f:
            yaml.dump(sample_legacy_config, f)
        
        hosts = config_manager.list_hosts()
        assert 'myhost' in hosts
        assert 'production' in hosts
        assert len(hosts) == 2
    
    def test_load_host_config_new_format(self, config_manager, sample_host_config):
        """Test load_host_config with new format."""
        config_manager.save_host_config('myhost', sample_host_config)
        
        loaded = config_manager.load_host_config('myhost')
        assert loaded['name'] == 'myhost'
        assert loaded['host'] == 'srv.example.host'
        assert 'apps' in loaded
        assert 'myapp' in loaded['apps']
    
    def test_load_host_config_legacy_format(self, config_manager, sample_legacy_config):
        """Test load_host_config with legacy format."""
        legacy_file = config_manager.apps_dir / 'production.yaml'
        with open(legacy_file, 'w') as f:
            yaml.dump(sample_legacy_config, f)
        
        loaded = config_manager.load_host_config('production')
        assert loaded['name'] == 'production'
        assert loaded['host'] == 'srv.example.com'

    def test_load_host_config_not_found(self, config_manager):
        """Test load_host_config with nonexistent host."""
        with pytest.raises(FileNotFoundError) as exc_info:
            config_manager.load_host_config('nonexistent')

        assert 'Host configuration not found' in str(exc_info.value)

    def test_delete_host_config(self, config_manager, sample_host_config):
        """Test delete_host_config."""
        config_manager.save_host_config('myhost', sample_host_config)
        assert config_manager.host_exists('myhost')

        config_manager.delete_host_config('myhost')
        assert not config_manager.host_exists('myhost')


class TestAppManagement:
    """Test app management methods."""

    def test_app_exists(self, config_manager, sample_host_config):
        """Test app_exists."""
        config_manager.save_host_config('myhost', sample_host_config)

        assert config_manager.app_exists('myhost', 'myapp')
        assert config_manager.app_exists('myhost', 'myapp-staging')
        assert not config_manager.app_exists('myhost', 'nonexistent')

    def test_list_apps(self, config_manager, sample_host_config):
        """Test list_apps."""
        config_manager.save_host_config('myhost', sample_host_config)

        apps = config_manager.list_apps('myhost')
        assert 'myapp' in apps
        assert 'myapp-staging' in apps
        assert len(apps) == 2

    def test_list_apps_empty(self, config_manager):
        """Test list_apps with no apps."""
        config = {
            'name': 'empty',
            'host': 'empty.com',
            'port': 22,
            'user': 'root',
            'ssh_key': '~/.ssh/empty'
        }
        config_manager.save_host_config('empty', config)

        apps = config_manager.list_apps('empty')
        assert len(apps) == 0

    def test_load_app_config(self, config_manager, sample_host_config):
        """Test load_app_config."""
        config_manager.save_host_config('myhost', sample_host_config)

        app = config_manager.load_app_config('myhost', 'myapp')
        assert app['database']['type'] == 'mysql'
        assert app['database']['name'] == 'myapp_db'
        assert app['webserver']['type'] == 'apache2'

    def test_load_app_config_not_found(self, config_manager, sample_host_config):
        """Test load_app_config with nonexistent app."""
        config_manager.save_host_config('myhost', sample_host_config)

        with pytest.raises(FileNotFoundError) as exc_info:
            config_manager.load_app_config('myhost', 'nonexistent')

        assert "App 'nonexistent' not found" in str(exc_info.value)

    def test_load_app_config_missing_webserver_type(self, config_manager):
        """Test load_app_config fails when webserver.type is missing."""
        config = {
            'name': 'test',
            'host': 'test.com',
            'port': 22,
            'user': 'root',
            'ssh_key': '~/.ssh/test',
            'apps': {
                'myapp': {
                    'database': {
                        'type': 'mysql'
                    }
                    # Missing webserver.type
                }
            }
        }
        config_manager.save_host_config('test', config)

        with pytest.raises(ValueError) as exc_info:
            config_manager.load_app_config('test', 'myapp')

        assert "Missing 'webserver.type'" in str(exc_info.value)
        assert "webserver.type: nginx" in str(exc_info.value)

    def test_save_app_config(self, config_manager, sample_host_config):
        """Test save_app_config."""
        config_manager.save_host_config('myhost', sample_host_config)

        new_app = {
            'database': {
                'type': 'postgresql',
                'name': 'ai_db'
            },
            'webserver': {
                'type': 'nginx'
            }
        }

        config_manager.save_app_config('myhost', 'ai', new_app)

        # Verify saved
        loaded = config_manager.load_app_config('myhost', 'ai')
        assert loaded['database']['type'] == 'postgresql'
        assert loaded['webserver']['type'] == 'nginx'

    def test_delete_app_config(self, config_manager, sample_host_config):
        """Test delete_app_config."""
        config_manager.save_host_config('myhost', sample_host_config)

        assert config_manager.app_exists('myhost', 'myapp')

        config_manager.delete_app_config('myhost', 'myapp')

        assert not config_manager.app_exists('myhost', 'myapp')
        assert config_manager.app_exists('myhost', 'myapp-staging')


class TestActiveContext:
    """Test active host/app management."""

    def test_set_get_active_host(self, config_manager, sample_host_config):
        """Test set_active_host and get_active_host."""
        config_manager.save_host_config('myhost', sample_host_config)

        config_manager.set_active_host('myhost')
        assert config_manager.get_active_host() == 'myhost'

    def test_set_active_host_not_found(self, config_manager):
        """Test set_active_host with nonexistent host."""
        with pytest.raises(ValueError) as exc_info:
            config_manager.set_active_host('nonexistent')

        assert "Host 'nonexistent' not found" in str(exc_info.value)

    def test_set_get_active_app(self, config_manager):
        """Test set_active_app and get_active_app."""
        config_manager.set_active_app('myapp')
        assert config_manager.get_active_app() == 'myapp'

    def test_set_active_context(self, config_manager, sample_host_config):
        """Test set_active_context."""
        config_manager.save_host_config('myhost', sample_host_config)

        config_manager.set_active_context('myhost', 'myapp')

        assert config_manager.get_active_host() == 'myhost'
        assert config_manager.get_active_app() == 'myapp'

    def test_set_active_context_invalid_host(self, config_manager):
        """Test set_active_context with invalid host."""
        with pytest.raises(ValueError) as exc_info:
            config_manager.set_active_context('nonexistent', 'myapp')

        assert "Host 'nonexistent' not found" in str(exc_info.value)

    def test_set_active_context_invalid_app(self, config_manager, sample_host_config):
        """Test set_active_context with invalid app."""
        config_manager.save_host_config('myhost', sample_host_config)

        with pytest.raises(ValueError) as exc_info:
            config_manager.set_active_context('myhost', 'nonexistent')

        assert "App 'nonexistent' not found" in str(exc_info.value)


class TestBackwardCompatibility:
    """Test backward compatibility with legacy format."""

    def test_load_app_config_legacy_format(self, config_manager, sample_legacy_config):
        """Test load_app_config with legacy format."""
        # Save to legacy directory
        legacy_file = config_manager.apps_dir / 'production.yaml'
        with open(legacy_file, 'w') as f:
            yaml.dump(sample_legacy_config, f)

        # Load as app (legacy format: host config IS app config)
        app = config_manager.load_app_config('production', 'production')
        assert app['database']['type'] == 'mysql'
        assert app['services']['web'] == 'nginx'

