"""
Tests for Issue #18: Configuration and Environment Management CLI

This test suite verifies the configuration management functionality including:
- Configuration display and management
- Project initialization functionality
- Configuration validation
- Integration with existing config system
- Environment variable handling
"""

import pytest
import tempfile
import shutil
import os
import json
import yaml
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock
from click.testing import CliRunner

from markitect.config_manager import ConfigurationManager
from markitect.cli import cli


class TestConfigurationManager:
    """Test the core ConfigurationManager functionality."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_dir = Path(self.temp_dir)
        self.original_cwd = os.getcwd()
        os.chdir(self.temp_dir)

    def teardown_method(self):
        """Clean up test environment."""
        os.chdir(self.original_cwd)
        shutil.rmtree(self.temp_dir)

    def test_get_current_config(self):
        """Test getting current configuration."""
        config_manager = ConfigurationManager()
        config = config_manager.get_current_config()

        # Should have basic configuration keys
        expected_keys = [
            'gitea_url', 'repo_owner', 'repo_name', 'api_token',
            'workspace_dir', 'database_path', 'cache_dir', 'tests_dir'
        ]

        for key in expected_keys:
            assert key in config

        # Should have metadata
        assert '_meta' in config
        assert 'config_sources' in config['_meta']
        assert 'env_variables' in config['_meta']
        assert 'working_directory' in config['_meta']

    def test_display_config_yaml_format(self):
        """Test configuration display in YAML format."""
        config_manager = ConfigurationManager()
        output = config_manager.display_config(output_format='yaml')

        # Should be valid YAML
        parsed = yaml.safe_load(output)
        assert isinstance(parsed, dict)
        assert 'gitea_url' in parsed

    def test_display_config_json_format(self):
        """Test configuration display in JSON format."""
        config_manager = ConfigurationManager()
        output = config_manager.display_config(output_format='json')

        # Should be valid JSON
        parsed = json.loads(output)
        assert isinstance(parsed, dict)
        assert 'gitea_url' in parsed

    def test_display_config_simple_format(self):
        """Test configuration display in simple format."""
        config_manager = ConfigurationManager()
        output = config_manager.display_config(output_format='simple')

        # Should contain key=value pairs
        lines = output.split('\n')
        assert any('gitea_url =' in line for line in lines)
        assert any('repo_owner =' in line for line in lines)

    def test_mask_sensitive_data(self):
        """Test masking of sensitive configuration data."""
        config_manager = ConfigurationManager()

        # Create config with sensitive data
        config = {
            'api_token': 'secret123',
            'password': 'mypassword',
            'gitea_url': 'http://localhost:3000',
            'repo_name': 'test-repo'
        }

        masked = config_manager._mask_sensitive_data(config)

        # Sensitive fields should be masked
        assert masked['api_token'] == '***MASKED***'
        assert masked['password'] == '***MASKED***'

        # Non-sensitive fields should remain
        assert masked['gitea_url'] == 'http://localhost:3000'
        assert masked['repo_name'] == 'test-repo'

    def test_mask_sensitive_data_preserves_empty_values(self):
        """Test that empty sensitive values are not masked."""
        config_manager = ConfigurationManager()

        config = {
            'api_token': '',
            'secret': None,
            'repo_name': 'test-repo'
        }

        masked = config_manager._mask_sensitive_data(config)

        # Empty/None values should not be masked
        assert masked['api_token'] == ''
        assert masked['secret'] is None
        assert masked['repo_name'] == 'test-repo'

    def test_set_config_value_simple(self):
        """Test setting a simple configuration value."""
        config_manager = ConfigurationManager()

        success = config_manager.set_config_value('repo_name', 'test-repository')
        assert success

        # Verify file was created
        config_file = self.test_dir / '.markitect.yml'
        assert config_file.exists()

        # Verify content
        content = yaml.safe_load(config_file.read_text())
        assert content['repo_name'] == 'test-repository'

    def test_set_config_value_nested(self):
        """Test setting nested configuration value with dot notation."""
        config_manager = ConfigurationManager()

        success = config_manager.set_config_value('gitea.url', 'http://example.com')
        assert success

        # Verify nested structure
        config_file = self.test_dir / '.markitect.yml'
        content = yaml.safe_load(config_file.read_text())
        assert content['gitea']['url'] == 'http://example.com'

    def test_set_config_value_type_conversion(self):
        """Test automatic type conversion for configuration values."""
        config_manager = ConfigurationManager()

        # Test boolean conversion
        config_manager.set_config_value('debug', 'true')
        config_manager.set_config_value('verbose', 'false')

        # Test number conversion
        config_manager.set_config_value('port', '3000')
        config_manager.set_config_value('timeout', '30.5')

        config_file = self.test_dir / '.markitect.yml'
        content = yaml.safe_load(config_file.read_text())

        assert content['debug'] is True
        assert content['verbose'] is False
        assert content['port'] == 3000
        assert content['timeout'] == 30.5

    def test_set_config_value_validation_error(self):
        """Test configuration validation during value setting."""
        config_manager = ConfigurationManager()

        # Test invalid URL
        with pytest.raises(ValueError, match="not a valid URL format"):
            config_manager.set_config_value('gitea_url', 'invalid-url')

    def test_set_config_value_existing_file(self):
        """Test setting values in existing configuration file."""
        # Create existing config file
        existing_config = {
            'repo_name': 'old-name',
            'gitea_url': 'http://localhost:3000'
        }
        config_file = self.test_dir / '.markitect.yml'
        config_file.write_text(yaml.dump(existing_config))

        config_manager = ConfigurationManager()
        success = config_manager.set_config_value('repo_name', 'new-name')
        assert success

        # Verify update
        content = yaml.safe_load(config_file.read_text())
        assert content['repo_name'] == 'new-name'
        assert content['gitea_url'] == 'http://localhost:3000'  # Should be preserved

    def test_set_config_value_custom_file(self):
        """Test setting values in custom configuration file."""
        custom_file = self.test_dir / 'custom.yml'

        config_manager = ConfigurationManager()
        success = config_manager.set_config_value('repo_name', 'test', str(custom_file))
        assert success

        assert custom_file.exists()
        content = yaml.safe_load(custom_file.read_text())
        assert content['repo_name'] == 'test'

    def test_initialize_project_config(self):
        """Test project configuration initialization."""
        config_manager = ConfigurationManager()
        result = config_manager.initialize_project_config(self.test_dir, interactive=False)

        # Verify config file created
        config_file = Path(result['config_file'])
        assert config_file.exists()
        assert config_file.name == '.markitect.yml'

        # Verify directories created
        assert len(result['created_directories']) > 0
        for directory in result['created_directories']:
            assert Path(directory).exists()

        # Verify config structure
        config = result['config']
        assert 'gitea_url' in config
        assert 'repo_name' in config
        assert config['repo_name'] == self.test_dir.name

    def test_initialize_project_config_custom_dir(self):
        """Test project initialization in custom directory."""
        project_dir = self.test_dir / 'my-project'

        config_manager = ConfigurationManager()
        result = config_manager.initialize_project_config(project_dir, interactive=False)

        # Verify correct location
        config_file = Path(result['config_file'])
        assert config_file.parent == project_dir
        assert project_dir.exists()

    def test_validate_configuration_success(self):
        """Test configuration validation with valid config."""
        config = {
            'gitea_url': 'http://localhost:3000',
            'database_path': str(self.test_dir / 'db.sqlite'),
            'workspace_dir': str(self.test_dir / 'workspace'),
            'cache_dir': str(self.test_dir / 'cache'),
            'tests_dir': str(self.test_dir / 'tests')
        }

        config_manager = ConfigurationManager()
        results = config_manager.validate_configuration(config)

        # Should have validation results
        assert len(results) > 0

        # Check for required field validations
        required_checks = [r for r in results if r['key'] in ['gitea_url', 'database_path']]
        assert len(required_checks) > 0

    def test_validate_configuration_missing_required(self):
        """Test configuration validation with missing required fields."""
        config = {
            'repo_name': 'test'
            # Missing gitea_url and database_path
        }

        config_manager = ConfigurationManager()
        results = config_manager.validate_configuration(config)

        # Should have errors for missing required fields
        errors = [r for r in results if r['status'] == 'error']
        assert len(errors) > 0

        error_keys = [r['key'] for r in errors]
        assert 'gitea_url' in error_keys or 'database_path' in error_keys

    def test_validate_configuration_path_creation(self):
        """Test configuration validation creates missing directories."""
        config = {
            'gitea_url': 'http://localhost:3000',
            'database_path': str(self.test_dir / 'data' / 'db.sqlite'),
            'workspace_dir': str(self.test_dir / 'new_workspace'),
            'cache_dir': str(self.test_dir / 'new_cache'),
            'tests_dir': str(self.test_dir / 'new_tests')
        }

        config_manager = ConfigurationManager()
        results = config_manager.validate_configuration(config)

        # Directories should be created
        assert (self.test_dir / 'new_workspace').exists()
        assert (self.test_dir / 'new_cache').exists()
        assert (self.test_dir / 'new_tests').exists()
        assert (self.test_dir / 'data').exists()  # Database parent directory

        # Should have warnings for created directories
        warnings = [r for r in results if r['status'] == 'warning']
        assert len(warnings) > 0

    def test_list_config_keys(self):
        """Test listing available configuration keys."""
        config_manager = ConfigurationManager()
        keys = config_manager.list_config_keys()

        # Should return list of tuples
        assert isinstance(keys, list)
        assert len(keys) > 0

        # Each item should be (key, description, default)
        for item in keys:
            assert len(item) == 3
            assert isinstance(item[0], str)  # key
            assert isinstance(item[1], str)  # description

        # Should include expected keys
        key_names = [item[0] for item in keys]
        assert 'gitea_url' in key_names
        assert 'repo_name' in key_names
        assert 'api_token' in key_names

    def test_get_config_help_specific_key(self):
        """Test getting help for specific configuration key."""
        config_manager = ConfigurationManager()
        help_text = config_manager.get_config_help('gitea_url')

        assert 'gitea_url' in help_text
        assert 'description' in help_text.lower() or ':' in help_text

    def test_get_config_help_unknown_key(self):
        """Test getting help for unknown configuration key."""
        config_manager = ConfigurationManager()
        help_text = config_manager.get_config_help('unknown_key')

        assert 'unknown' in help_text.lower()

    def test_get_config_help_general(self):
        """Test getting general configuration help."""
        config_manager = ConfigurationManager()
        help_text = config_manager.get_config_help()

        assert 'available' in help_text.lower() or 'configuration' in help_text.lower()
        assert 'gitea_url' in help_text
        assert 'repo_name' in help_text

    def test_convert_value_booleans(self):
        """Test value conversion for boolean types."""
        config_manager = ConfigurationManager()

        # True values
        for true_val in ['true', 'True', 'TRUE', 'yes', 'YES', 'on', 'ON', '1']:
            assert config_manager._convert_value(true_val) is True

        # False values
        for false_val in ['false', 'False', 'FALSE', 'no', 'NO', 'off', 'OFF', '0']:
            assert config_manager._convert_value(false_val) is False

    def test_convert_value_numbers(self):
        """Test value conversion for numeric types."""
        config_manager = ConfigurationManager()

        # Integers
        assert config_manager._convert_value('42') == 42
        assert config_manager._convert_value('-10') == -10

        # Floats
        assert config_manager._convert_value('3.14') == 3.14
        assert config_manager._convert_value('-2.5') == -2.5

        # Strings that look like numbers but aren't
        assert config_manager._convert_value('not-a-number') == 'not-a-number'

    def test_is_valid_url(self):
        """Test URL validation."""
        config_manager = ConfigurationManager()

        # Valid URLs
        valid_urls = [
            'http://localhost:3000',
            'https://example.com',
            'http://192.168.1.1:8080',
            'https://api.github.com/repos'
        ]
        for url in valid_urls:
            assert config_manager._is_valid_url(url), f"Should be valid: {url}"

        # Invalid URLs
        invalid_urls = [
            'not-a-url',
            'ftp://example.com',
            'localhost:3000',
            'http://',
            ''
        ]
        for url in invalid_urls:
            assert not config_manager._is_valid_url(url), f"Should be invalid: {url}"

    def test_is_valid_path(self):
        """Test path validation."""
        config_manager = ConfigurationManager()

        # Valid paths
        valid_paths = [
            '/absolute/path',
            './relative/path',
            '~/home/path',
            'simple-path',
            str(self.test_dir / 'subdir')
        ]
        for path in valid_paths:
            assert config_manager._is_valid_path(path), f"Should be valid: {path}"

        # Edge case: empty string (should be considered valid as it can represent current directory)
        assert config_manager._is_valid_path('')


class TestConfigFileParsing:
    """Test configuration file parsing and saving."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_dir = Path(self.temp_dir)
        self.original_cwd = os.getcwd()
        os.chdir(self.temp_dir)

    def teardown_method(self):
        """Clean up test environment."""
        os.chdir(self.original_cwd)
        shutil.rmtree(self.temp_dir)

    def test_load_yaml_config_file(self):
        """Test loading YAML configuration file."""
        config_content = {
            'gitea_url': 'http://localhost:3000',
            'repo_name': 'test-repo',
            'nested': {
                'key': 'value'
            }
        }

        config_file = self.test_dir / 'test.yml'
        config_file.write_text(yaml.dump(config_content))

        config_manager = ConfigurationManager()
        loaded = config_manager._load_config_file(config_file)

        assert loaded == config_content

    def test_load_json_config_file(self):
        """Test loading JSON configuration file."""
        config_content = {
            'gitea_url': 'http://localhost:3000',
            'repo_name': 'test-repo',
            'debug': True
        }

        config_file = self.test_dir / 'test.json'
        config_file.write_text(json.dumps(config_content))

        config_manager = ConfigurationManager()
        loaded = config_manager._load_config_file(config_file)

        assert loaded == config_content

    def test_save_yaml_config_file(self):
        """Test saving YAML configuration file."""
        config_content = {
            'gitea_url': 'http://localhost:3000',
            'repo_name': 'test-repo'
        }

        config_file = self.test_dir / 'output.yml'
        config_manager = ConfigurationManager()
        config_manager._save_config_file(config_content, config_file)

        assert config_file.exists()
        loaded = yaml.safe_load(config_file.read_text())
        assert loaded == config_content

    def test_save_json_config_file(self):
        """Test saving JSON configuration file."""
        config_content = {
            'gitea_url': 'http://localhost:3000',
            'repo_name': 'test-repo'
        }

        config_file = self.test_dir / 'output.json'
        config_manager = ConfigurationManager()
        config_manager._save_config_file(config_content, config_file)

        assert config_file.exists()
        loaded = json.loads(config_file.read_text())
        assert loaded == config_content

    def test_load_invalid_config_file(self):
        """Test loading invalid configuration file."""
        config_file = self.test_dir / 'invalid.yml'
        config_file.write_text('invalid: yaml: content: [')

        config_manager = ConfigurationManager()
        with pytest.raises(ValueError, match="Failed to load config file"):
            config_manager._load_config_file(config_file)

    def test_get_target_config_file_existing(self):
        """Test getting target config file when one exists."""
        # Create an existing config file
        existing_file = self.test_dir / '.markitect.yml'
        existing_file.write_text('test: value')

        config_manager = ConfigurationManager()
        target = config_manager._get_target_config_file()

        # Should be relative path that matches the existing file
        assert target.name == existing_file.name
        assert target.exists()

    def test_get_target_config_file_default(self):
        """Test getting target config file when none exists."""
        config_manager = ConfigurationManager()
        target = config_manager._get_target_config_file()

        # Should be the default config file name
        assert target.name == '.markitect.yml'

    def test_get_target_config_file_custom(self):
        """Test getting custom target config file."""
        custom_file = 'custom-config.yml'

        config_manager = ConfigurationManager()
        target = config_manager._get_target_config_file(custom_file)

        assert target == Path(custom_file)


class TestCLIIntegration:
    """Test CLI command integration."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_dir = Path(self.temp_dir)
        self.original_cwd = os.getcwd()
        os.chdir(self.temp_dir)
        self.runner = CliRunner()

    def teardown_method(self):
        """Clean up test environment."""
        os.chdir(self.original_cwd)
        shutil.rmtree(self.temp_dir)

    def test_config_show_command(self):
        """Test config-show CLI command."""
        result = self.runner.invoke(cli, ['config-show'])

        assert result.exit_code == 0
        assert 'gitea_url' in result.output

    def test_config_show_command_json_format(self):
        """Test config-show with JSON format."""
        result = self.runner.invoke(cli, ['config-show', '--format', 'json'])

        assert result.exit_code == 0
        # Output should be valid JSON
        try:
            json.loads(result.output)
        except json.JSONDecodeError:
            pytest.fail("Output is not valid JSON")

    def test_config_show_command_simple_format(self):
        """Test config-show with simple format."""
        result = self.runner.invoke(cli, ['config-show', '--format', 'simple'])

        assert result.exit_code == 0
        assert '=' in result.output  # Should contain key=value pairs

    def test_config_set_command(self):
        """Test config-set CLI command."""
        result = self.runner.invoke(cli, [
            'config-set', 'repo_name', 'test-repository'
        ])

        assert result.exit_code == 0
        assert 'Configuration updated' in result.output

        # Verify file was created
        config_file = self.test_dir / '.markitect.yml'
        assert config_file.exists()

        content = yaml.safe_load(config_file.read_text())
        assert content['repo_name'] == 'test-repository'

    def test_config_set_command_nested_key(self):
        """Test config-set with nested key."""
        result = self.runner.invoke(cli, [
            'config-set', 'gitea.url', 'http://example.com'
        ])

        assert result.exit_code == 0

        config_file = self.test_dir / '.markitect.yml'
        content = yaml.safe_load(config_file.read_text())
        assert content['gitea']['url'] == 'http://example.com'

    def test_config_set_command_custom_file(self):
        """Test config-set with custom config file."""
        custom_file = 'custom.yml'
        result = self.runner.invoke(cli, [
            'config-set', 'repo_name', 'test',
            '--config-file', custom_file
        ])

        assert result.exit_code == 0
        assert Path(custom_file).exists()

    def test_config_set_command_no_validate(self):
        """Test config-set with validation disabled."""
        result = self.runner.invoke(cli, [
            'config-set', 'repo_name', 'test',
            '--no-validate'
        ])

        assert result.exit_code == 0

    def test_config_set_command_validation_error(self):
        """Test config-set with validation error."""
        result = self.runner.invoke(cli, [
            'config-set', 'gitea_url', 'invalid-url'
        ])

        assert result.exit_code == 1
        assert 'Configuration error' in result.output

    def test_config_init_command_non_interactive(self):
        """Test config-init CLI command in non-interactive mode."""
        result = self.runner.invoke(cli, [
            'config-init', '--no-interactive'
        ])

        assert result.exit_code == 0
        assert 'initialized successfully' in result.output

        # Verify config file created
        config_file = self.test_dir / '.markitect.yml'
        assert config_file.exists()

        # Verify directories created
        assert (self.test_dir / '.markitect_workspace').exists()
        assert (self.test_dir / '.ast_cache').exists()
        assert (self.test_dir / 'tests').exists()

    def test_config_init_command_custom_directory(self):
        """Test config-init with custom project directory."""
        project_dir = self.test_dir / 'my-project'

        result = self.runner.invoke(cli, [
            'config-init',
            '--project-dir', str(project_dir),
            '--no-interactive'
        ])

        assert result.exit_code == 0

        # Verify in correct location
        config_file = project_dir / '.markitect.yml'
        assert config_file.exists()

    def test_config_init_command_existing_file_no_force(self):
        """Test config-init with existing file without force."""
        # Create existing config file
        config_file = self.test_dir / '.markitect.yml'
        config_file.write_text('existing: config')

        result = self.runner.invoke(cli, [
            'config-init', '--no-interactive'
        ])

        assert result.exit_code == 1
        assert 'already exists' in result.output

    def test_config_init_command_existing_file_with_force(self):
        """Test config-init with existing file with force."""
        # Create existing config file
        config_file = self.test_dir / '.markitect.yml'
        config_file.write_text('existing: config')

        result = self.runner.invoke(cli, [
            'config-init', '--no-interactive', '--force'
        ])

        assert result.exit_code == 0
        assert 'initialized successfully' in result.output

    def test_config_validate_command(self):
        """Test config-validate CLI command."""
        result = self.runner.invoke(cli, ['config-validate'])

        assert result.exit_code in [0, 1]  # May have warnings/errors
        assert 'Configuration Validation Summary' in result.output

    def test_config_validate_command_verbose(self):
        """Test config-validate with verbose output."""
        result = self.runner.invoke(cli, ['config-validate', '--verbose'])

        assert result.exit_code in [0, 1]
        assert 'Configuration Validation Summary' in result.output

    def test_config_help_command_general(self):
        """Test config-help CLI command."""
        result = self.runner.invoke(cli, ['config-help'])

        assert result.exit_code == 0
        assert 'available' in result.output.lower() or 'configuration' in result.output.lower()
        assert 'gitea_url' in result.output

    def test_config_help_command_specific_key(self):
        """Test config-help for specific key."""
        result = self.runner.invoke(cli, ['config-help', 'gitea_url'])

        assert result.exit_code == 0
        assert 'gitea_url' in result.output

    def test_config_help_command_unknown_key(self):
        """Test config-help for unknown key."""
        result = self.runner.invoke(cli, ['config-help', 'unknown_key'])

        assert result.exit_code == 0
        assert 'unknown' in result.output.lower()


class TestEnvironmentVariables:
    """Test environment variable handling."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_dir = Path(self.temp_dir)
        self.original_cwd = os.getcwd()
        os.chdir(self.temp_dir)

        # Store original environment
        self.original_env = dict(os.environ)

    def teardown_method(self):
        """Clean up test environment."""
        os.chdir(self.original_cwd)
        shutil.rmtree(self.temp_dir)

        # Restore original environment
        os.environ.clear()
        os.environ.update(self.original_env)

    def test_get_relevant_env_vars(self):
        """Test getting MARKITECT-related environment variables."""
        # Set some test environment variables
        os.environ['MARKITECT_GITEA_URL'] = 'http://test.com'
        os.environ['MARKITECT_REPO_NAME'] = 'test-repo'
        os.environ['OTHER_VAR'] = 'should-be-ignored'

        config_manager = ConfigurationManager()
        env_vars = config_manager._get_relevant_env_vars()

        assert 'MARKITECT_GITEA_URL' in env_vars
        assert 'MARKITECT_REPO_NAME' in env_vars
        assert 'OTHER_VAR' not in env_vars
        assert env_vars['MARKITECT_GITEA_URL'] == 'http://test.com'

    def test_config_with_env_vars(self):
        """Test configuration loading with environment variables."""
        # Set environment variables
        os.environ['MARKITECT_GITEA_URL'] = 'http://env-test.com'
        os.environ['MARKITECT_REPO_NAME'] = 'env-repo'

        config_manager = ConfigurationManager()
        config = config_manager.get_current_config()

        # Environment variables should be reflected in config
        assert config['gitea_url'] == 'http://env-test.com'
        assert config['repo_name'] == 'env-repo'

        # Should have env vars in metadata
        assert 'MARKITECT_GITEA_URL' in config['_meta']['env_variables']
        assert 'MARKITECT_REPO_NAME' in config['_meta']['env_variables']


class TestEdgeCases:
    """Test edge cases and error conditions."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_dir = Path(self.temp_dir)
        self.original_cwd = os.getcwd()
        os.chdir(self.temp_dir)

    def teardown_method(self):
        """Clean up test environment."""
        os.chdir(self.original_cwd)
        shutil.rmtree(self.temp_dir)

    def test_config_manager_with_permission_error(self):
        """Test handling permission errors."""
        # Create a directory we can't write to
        restricted_dir = self.test_dir / 'restricted'
        restricted_dir.mkdir(mode=0o444)  # Read-only

        config_manager = ConfigurationManager()

        # This should not crash, but may warn about permission issues
        try:
            config = config_manager.get_current_config()
            assert isinstance(config, dict)
        except PermissionError:
            # Acceptable to fail with permission error
            pass

    def test_set_config_value_invalid_file_path(self):
        """Test setting config value with invalid file path."""
        config_manager = ConfigurationManager()

        # Try to write to a path that doesn't exist and can't be created
        # Use a more reliable invalid path that doesn't depend on system permissions
        invalid_path = '/nonexistent_directory_12345/config.yml'
        with pytest.raises(ValueError):
            config_manager.set_config_value('test', 'value', invalid_path)

    def test_validate_configuration_with_none(self):
        """Test configuration validation with None input."""
        config_manager = ConfigurationManager()

        # Should not crash with None input
        results = config_manager.validate_configuration(None)
        assert isinstance(results, list)

    def test_mask_sensitive_data_with_complex_structure(self):
        """Test masking sensitive data in complex nested structure."""
        config_manager = ConfigurationManager()

        complex_config = {
            'database': {
                'password': 'secret123',
                'host': 'localhost'
            },
            'apis': [
                {'name': 'github', 'token': 'ghp_secret'},
                {'name': 'gitea', 'url': 'http://localhost:3000'}
            ],
            'secrets': {
                'nested': {
                    'api_key': 'very_secret'
                }
            }
        }

        masked = config_manager._mask_sensitive_data(complex_config)

        # Sensitive fields should be masked at any level
        assert masked['database']['password'] == '***MASKED***'
        assert masked['apis'][0]['token'] == '***MASKED***'

        # The 'secrets' key itself gets masked because it's a sensitive keyword
        # This is actually correct behavior
        assert masked['secrets'] == '***MASKED***'

        # Non-sensitive fields should remain
        assert masked['database']['host'] == 'localhost'
        assert masked['apis'][1]['url'] == 'http://localhost:3000'

    def test_get_config_sources_empty_directory(self):
        """Test getting config sources in empty directory."""
        config_manager = ConfigurationManager()
        sources = config_manager._get_config_sources()

        # Should always include defaults
        assert len(sources) > 0
        assert any('defaults' in source.lower() for source in sources)

    def test_initialize_project_config_existing_directories(self):
        """Test project initialization with existing directories."""
        # Pre-create some directories
        (self.test_dir / '.markitect_workspace').mkdir()
        (self.test_dir / 'tests').mkdir()

        config_manager = ConfigurationManager()
        result = config_manager.initialize_project_config(self.test_dir, interactive=False)

        # Should succeed even with existing directories
        assert 'config_file' in result
        assert Path(result['config_file']).exists()