import unittest
from unittest.mock import patch, Mock, MagicMock
import configparser
import tempfile
from pathlib import Path

from pygeai.cli.commands.welcome import (
    validate_credentials_file,
    display_validation_results,
    welcome_wizard,
    welcome_options
)


class TestValidateCredentialsFile(unittest.TestCase):
    """
    Tests for credential file validation.
    
    Run with:
    python -m unittest pygeai.tests.cli.commands.test_welcome.TestValidateCredentialsFile
    """

    def setUp(self):
        """Create a temporary directory for test credentials files."""
        self.test_dir = Path(tempfile.mkdtemp())
        self.creds_file = self.test_dir / 'credentials'

    def tearDown(self):
        """Clean up temporary test directory."""
        import shutil
        if self.test_dir.exists():
            shutil.rmtree(self.test_dir)

    def _create_credentials_file(self, content: str):
        """Helper to create a credentials file with given content."""
        self.creds_file.write_text(content)

    def _patch_settings_dir(self, func):
        """Helper to patch SETTINGS_DIR for testing."""
        import pygeai.cli.commands.welcome as welcome_module
        original_settings = welcome_module.SETTINGS_DIR
        welcome_module.SETTINGS_DIR = self.test_dir
        try:
            func()
        finally:
            welcome_module.SETTINGS_DIR = original_settings

    def test_credentials_file_not_found(self):
        """Test validation when credentials file doesn't exist."""
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertIn('Credentials file not found', result['errors'])
            self.assertEqual(result['auth_type'], 'none')

        self._patch_settings_dir(test)

    def test_invalid_ini_syntax(self):
        """Test validation with malformed INI file."""
        self._create_credentials_file('[default\nkey = value')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertTrue(any('Invalid INI syntax' in err for err in result['errors']))

        self._patch_settings_dir(test)

    def test_alias_not_found(self):
        """Test validation when specified alias doesn't exist."""
        self._create_credentials_file('[production]\ngeai_api_key = test')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertIn("Alias 'default' not found in credentials file", result['errors'])

        self._patch_settings_dir(test)

    def test_api_key_with_value_oauth_empty(self):
        """Test API key auth with empty OAuth token (should not warn about both)."""
        self._create_credentials_file('''[default]
geai_api_key = test_key_123
geai_api_base_url = https://api.test.com
geai_oauth_access_token = 
geai_project_id = 
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'api_key')
            # Should NOT have warning about both auth methods
            self.assertFalse(any('Both API key and OAuth' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)

    def test_oauth_with_value_api_key_empty(self):
        """Test OAuth auth with empty API key (should not warn about both)."""
        self._create_credentials_file('''[default]
geai_api_key = 
geai_oauth_access_token = test_token_456
geai_project_id = proj_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'oauth')
            # Should NOT have warning about both auth methods
            self.assertFalse(any('Both API key and OAuth' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)

    def test_both_auth_methods_with_values(self):
        """Test when both API key and OAuth have values (should warn)."""
        self._create_credentials_file('''[default]
geai_api_key = test_key_123
geai_oauth_access_token = test_token_456
geai_project_id = proj_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'api_key')
            # SHOULD have warning about both auth methods
            self.assertTrue(any('Both API key and OAuth token found with values' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)

    def test_both_auth_methods_empty(self):
        """Test when both auth methods are empty (should error)."""
        self._create_credentials_file('''[default]
geai_api_key = 
geai_oauth_access_token = 
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'none')
            self.assertTrue(any('No authentication method configured' in err for err in result['errors']))

        self._patch_settings_dir(test)

    def test_api_key_empty_value(self):
        """Test API key validation when key exists but is empty."""
        self._create_credentials_file('''[default]
geai_api_key = 
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'none')

        self._patch_settings_dir(test)

    def test_api_key_valid(self):
        """Test valid API key authentication."""
        self._create_credentials_file('''[default]
geai_api_key = valid_key_123
geai_api_base_url = https://api.test.com
geai_api_eval_url = https://eval.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'api_key')
            self.assertEqual(len(result['errors']), 0)

        self._patch_settings_dir(test)

    def test_api_key_missing_base_url(self):
        """Test API key validation when base URL is missing."""
        self._create_credentials_file('''[default]
geai_api_key = valid_key_123
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'api_key')
            self.assertTrue(any('GEAI_API_BASE_URL is required' in err for err in result['errors']))

        self._patch_settings_dir(test)

    def test_api_key_empty_base_url(self):
        """Test API key validation when base URL is empty."""
        self._create_credentials_file('''[default]
geai_api_key = valid_key_123
geai_api_base_url = 
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'api_key')
            self.assertTrue(any('GEAI_API_BASE_URL is empty' in err for err in result['errors']))

        self._patch_settings_dir(test)

    def test_oauth_valid(self):
        """Test valid OAuth authentication."""
        self._create_credentials_file('''[default]
geai_oauth_access_token = valid_token_123
geai_project_id = proj_123
geai_api_base_url = https://api.test.com
geai_organization_id = org_456
geai_api_eval_url = https://eval.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'oauth')
            self.assertEqual(len(result['errors']), 0)

        self._patch_settings_dir(test)

    def test_oauth_empty_token(self):
        """Test OAuth validation when token is empty."""
        self._create_credentials_file('''[default]
geai_oauth_access_token = 
geai_project_id = proj_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'none')

        self._patch_settings_dir(test)

    def test_oauth_missing_project_id(self):
        """Test OAuth validation when project ID is missing."""
        self._create_credentials_file('''[default]
geai_oauth_access_token = valid_token_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'oauth')
            self.assertTrue(any('GEAI_PROJECT_ID is required' in err for err in result['errors']))

        self._patch_settings_dir(test)

    def test_oauth_empty_project_id(self):
        """Test OAuth validation when project ID is empty."""
        self._create_credentials_file('''[default]
geai_oauth_access_token = valid_token_123
geai_project_id = 
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertFalse(result['valid'])
            self.assertEqual(result['auth_type'], 'oauth')
            self.assertTrue(any('GEAI_PROJECT_ID is empty' in err for err in result['errors']))

        self._patch_settings_dir(test)

    def test_oauth_missing_organization_id_warning(self):
        """Test OAuth validation warns when organization ID is missing."""
        self._create_credentials_file('''[default]
geai_oauth_access_token = valid_token_123
geai_project_id = proj_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'oauth')
            self.assertTrue(any('GEAI_ORGANIZATION_ID' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)

    def test_missing_eval_url_warning(self):
        """Test that missing eval URL generates a warning."""
        self._create_credentials_file('''[default]
geai_api_key = valid_key_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertTrue(any('GEAI_API_EVAL_URL' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)

    def test_empty_eval_url_warning(self):
        """Test that empty eval URL generates a warning."""
        self._create_credentials_file('''[default]
geai_api_key = valid_key_123
geai_api_base_url = https://api.test.com
geai_api_eval_url = 
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertTrue(any('GEAI_API_EVAL_URL' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)

    def test_only_api_key_field_no_oauth(self):
        """Test when only API key field exists (no OAuth field at all)."""
        self._create_credentials_file('''[default]
geai_api_key = valid_key_123
geai_api_base_url = https://api.test.com
''')
        
        def test():
            result = validate_credentials_file('default')
            self.assertTrue(result['valid'])
            self.assertEqual(result['auth_type'], 'api_key')
            # Should NOT warn about both auth methods
            self.assertFalse(any('Both API key and OAuth' in warn for warn in result['warnings']))

        self._patch_settings_dir(test)


class TestDisplayValidationResults(unittest.TestCase):
    """
    Tests for display_validation_results function.
    
    Run with:
    python -m unittest pygeai.tests.cli.commands.test_welcome.TestDisplayValidationResults
    """

    @patch('pygeai.core.utils.console.Console.write_success')
    @patch('pygeai.core.utils.console.Console.write_stdout')
    def test_display_valid_results(self, mock_stdout, mock_success):
        """Test displaying valid validation results."""
        validation = {
            'valid': True,
            'auth_type': 'api_key',
            'errors': [],
            'warnings': []
        }
        
        with patch('pygeai.health.clients.HealthClient') as mock_health:
            mock_health.return_value.check_api_status.return_value = {'running': True}
            display_validation_results(validation)
            
            mock_success.assert_called()
            self.assertTrue(any('Configuration validated successfully' in str(call) 
                              for call in mock_success.call_args_list))

    @patch('pygeai.core.utils.console.Console.write_stderr')
    def test_display_invalid_results(self, mock_stderr):
        """Test displaying invalid validation results."""
        validation = {
            'valid': False,
            'auth_type': 'none',
            'errors': ['Test error 1', 'Test error 2'],
            'warnings': []
        }
        
        display_validation_results(validation)
        
        mock_stderr.assert_called()
        self.assertTrue(any('Configuration validation failed' in str(call) 
                          for call in mock_stderr.call_args_list))

    @patch('pygeai.core.utils.console.Console.write_warning')
    @patch('pygeai.core.utils.console.Console.write_success')
    @patch('pygeai.core.utils.console.Console.write_stdout')
    def test_display_warnings(self, mock_stdout, mock_success, mock_warning):
        """Test displaying validation warnings."""
        validation = {
            'valid': True,
            'auth_type': 'api_key',
            'errors': [],
            'warnings': ['Warning 1', 'Warning 2']
        }
        
        with patch('pygeai.health.clients.HealthClient') as mock_health:
            mock_health.return_value.check_api_status.return_value = {'running': True}
            display_validation_results(validation)
            
            mock_warning.assert_called()
            self.assertEqual(mock_warning.call_count, 2)


class TestWelcomeWizard(unittest.TestCase):
    """
    Tests for welcome_wizard function.
    
    Run with:
    python -m unittest pygeai.tests.cli.commands.test_welcome.TestWelcomeWizard
    """

    @patch('pygeai.cli.commands.welcome.reset_first_run')
    @patch('pygeai.core.utils.console.Console.write_success')
    def test_reset_option(self, mock_write, mock_reset):
        """Test that --reset option resets first-run marker."""
        from pygeai.cli.commands import Option
        
        reset_option = Option('reset', ['--reset'], 'Reset first-run flag', False)
        option_list = [(reset_option, None)]
        
        welcome_wizard(option_list)
        
        mock_reset.assert_called_once()
        mock_write.assert_called_once()

    @patch('pygeai.cli.commands.welcome.check_and_setup_credentials')
    @patch('pygeai.cli.commands.welcome.feature_tour')
    @patch('pygeai.cli.commands.welcome.show_resources')
    @patch('pygeai.cli.commands.welcome.mark_first_run_complete')
    @patch('pygeai.core.utils.console.Console.write_stdout')
    @patch('builtins.input', return_value='')
    def test_full_wizard(self, mock_input, mock_stdout, mock_mark, mock_resources, 
                        mock_tour, mock_creds):
        """Test full wizard flow."""
        welcome_wizard(option_list=[])
        
        mock_creds.assert_called_once()
        mock_tour.assert_called_once()
        mock_resources.assert_called_once()
        mock_mark.assert_called_once()

    @patch('pygeai.cli.commands.welcome.check_and_setup_credentials')
    @patch('pygeai.cli.commands.welcome.feature_tour')
    @patch('pygeai.cli.commands.welcome.mark_first_run_complete')
    @patch('pygeai.core.utils.console.Console.write_stdout')
    @patch('builtins.input', return_value='')
    def test_quick_mode(self, mock_input, mock_stdout, mock_mark, mock_tour, mock_creds):
        """Test quick mode skips feature tour."""
        from pygeai.cli.commands import Option
        
        quick_option = Option('quick', ['--quick'], 'Quick mode', False)
        option_list = [(quick_option, None)]
        
        welcome_wizard(option_list)
        
        mock_creds.assert_called_once()
        mock_tour.assert_not_called()
        mock_mark.assert_called_once()

    @patch('pygeai.cli.commands.welcome.check_and_setup_credentials')
    @patch('pygeai.cli.commands.welcome.feature_tour')
    @patch('pygeai.cli.commands.welcome.show_resources')
    @patch('pygeai.cli.commands.welcome.mark_first_run_complete')
    @patch('pygeai.core.utils.console.Console.write_stdout')
    @patch('builtins.input', return_value='')
    def test_tour_only_mode(self, mock_input, mock_stdout, mock_mark, mock_resources, mock_tour, mock_creds):
        """Test tour-only mode skips credential setup but shows tour."""
        from pygeai.cli.commands import Option
        
        tour_option = Option('tour', ['--tour'], 'Tour only', False)
        option_list = [(tour_option, None)]
        
        welcome_wizard(option_list)
        
        mock_creds.assert_not_called()
        mock_tour.assert_called_once()
        mock_resources.assert_called_once()
        mock_mark.assert_called_once()


class TestWelcomeOptions(unittest.TestCase):
    """
    Tests for welcome command options.
    
    Run with:
    python -m unittest pygeai.tests.cli.commands.test_welcome.TestWelcomeOptions
    """

    def test_options_defined(self):
        """Test that all expected options are defined."""
        option_names = [opt.name for opt in welcome_options]
        
        self.assertIn('quick', option_names)
        self.assertIn('tour', option_names)
        self.assertIn('no_setup', option_names)
        self.assertIn('reset', option_names)

    def test_quick_option_identifiers(self):
        """Test quick option has correct identifiers."""
        quick_opt = next(opt for opt in welcome_options if opt.name == 'quick')
        self.assertIn('--quick', quick_opt.identifiers)
        self.assertIn('-q', quick_opt.identifiers)

    def test_tour_option_identifiers(self):
        """Test tour option has correct identifiers."""
        tour_opt = next(opt for opt in welcome_options if opt.name == 'tour')
        self.assertIn('--tour', tour_opt.identifiers)
        self.assertIn('-t', tour_opt.identifiers)

    def test_reset_option_identifier(self):
        """Test reset option has correct identifier."""
        reset_opt = next(opt for opt in welcome_options if opt.name == 'reset')
        self.assertIn('--reset', reset_opt.identifiers)

    def test_no_setup_option_identifier(self):
        """Test no_setup option has correct identifier."""
        no_setup_opt = next(opt for opt in welcome_options if opt.name == 'no_setup')
        self.assertIn('--no-setup', no_setup_opt.identifiers)


if __name__ == '__main__':
    unittest.main()
