"""Tests for the test dashboard to ensure it's working correctly.

This test verifies that:
1. The dashboard detects all test files
2. The dashboard reports accurate test counts
3. The dashboard handles missing tests correctly
4. The dashboard environment loading works
"""

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

# Environment variable to control dashboard tests
RUN_DASHBOARD_TESTS = os.getenv("RUN_DASHBOARD_TESTS") == "1"

# Skip dashboard tests by default unless explicitly enabled
pytestmark = pytest.mark.skipif(
    not RUN_DASHBOARD_TESTS,
    reason="Dashboard tests are disabled by default; set RUN_DASHBOARD_TESTS=1 to run."
)

# Add scripts to path for imports
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "scripts"))

from dashboard import AITestDashboard


@pytest.mark.dashboard
class TestTestDashboard:
    """Test the test dashboard itself."""
    
    def test_dashboard_initialization(self):
        """Test that dashboard initializes correctly."""
        dashboard = AITestDashboard()
        
        assert dashboard.test_results == []
        assert dashboard.module_support == []
        assert dashboard.start_time is not None
    
    def test_load_env_file(self):
        """Test environment file loading."""
        dashboard = AITestDashboard()
        
        # Create a temporary .env file
        with tempfile.NamedTemporaryFile(mode='w', suffix='.env', delete=False) as f:
            f.write("TEST_VAR=test_value\n")
            f.write("ANOTHER_VAR=another_value\n")
            temp_env_path = f.name
        
        try:
            # Mock the env file path to our temp file
            with patch.object(Path, 'exists', return_value=True):
                with patch('builtins.open', return_value=open(temp_env_path)):
                    dashboard._load_env_file()
            
            # Check that environment variables were loaded
            assert os.getenv('TEST_VAR') == 'test_value'
            assert os.getenv('ANOTHER_VAR') == 'another_value'
            
        finally:
            # Cleanup
            if os.path.exists(temp_env_path):
                os.unlink(temp_env_path)
            # Clean up environment
            os.environ.pop('TEST_VAR', None)
            os.environ.pop('ANOTHER_VAR', None)
    
    def test_load_env_file_missing(self):
        """Test behavior when .env file is missing."""
        dashboard = AITestDashboard()
        
        with patch.object(Path, 'exists', return_value=False):
            dashboard._load_env_file()
        
        # Should not crash, just print warning
    
    def test_parse_pytest_output_success(self):
        """Test parsing pytest output with all tests passed."""
        dashboard = AITestDashboard()
        
        output = """
============================= test session starts ==============================
collected 24 items

tests/test_files_api.py::test_upload_file_success PASSED
tests/test_files_api.py::test_download_file_success PASSED
...
============================== 24 passed in 2.45s ==============================
        """
        
        passed, failed, skipped, errors = dashboard._parse_pytest_output(output)
        
        assert passed == 24
        assert failed == 0
        assert skipped == 0
        assert errors == 0
    
    def test_parse_pytest_output_with_failures(self):
        """Test parsing pytest output with failures."""
        dashboard = AITestDashboard()
        
        output = """
============================= test session starts ==============================
collected 10 items

tests/test_integration.py::test_upload PASSED
tests/test_integration.py::test_download FAILED
tests/test_integration.py::test_list SKIPPED
============================== 1 passed, 1 failed, 1 skipped in 1.23s ==============================
        """
        
        passed, failed, skipped, errors = dashboard._parse_pytest_output(output)
        
        assert passed == 1
        assert failed == 1
        assert skipped == 1
        assert errors == 0
    
    def test_parse_pytest_output_empty(self):
        """Test parsing empty pytest output."""
        dashboard = AITestDashboard()
        
        passed, failed, skipped, errors = dashboard._parse_pytest_output("")
        
        assert passed == 0
        assert failed == 0
        assert skipped == 0
        assert errors == 0
    
    def test_parse_pytest_output_non_string(self):
        """Test parsing non-string pytest output."""
        dashboard = AITestDashboard()
        
        passed, failed, skipped, errors = dashboard._parse_pytest_output(None)
        
        assert passed == 0
        assert failed == 0
        assert skipped == 0
        assert errors == 0
    
    @patch('scripts.dashboard.subprocess.Popen')
    def test_run_test_suite_success(self, mock_popen):
        """Test running a test suite successfully."""
        dashboard = AITestDashboard()
        
        # Mock the process and its communicate method
        mock_process = MagicMock()
        mock_process.stdout = ["============================== 5 passed in 1.0s ==============================\n"]
        mock_process.poll.return_value = 0  # Process has finished
        mock_process.returncode = 0
        mock_popen.return_value = mock_process
        
        dashboard._run_test_suite("Test Category", ["pytest", "test_file.py"], verbose=False)
        
        assert len(dashboard.test_results) == 1
        result = dashboard.test_results[0]
        assert result.category == "Test Category"
        assert result.total == 5
        assert result.passed == 5
        assert result.failed == 0
    
    @patch('scripts.dashboard.subprocess.Popen')
    def test_run_test_suite_with_failures(self, mock_popen):
        """Test running a test suite with failures."""
        dashboard = AITestDashboard()
        
        # Mock pytest run with failures
        mock_process = MagicMock()
        mock_process.stdout = [
            "============================== 3 passed, 2 failed in 1.5s ==============================\n"
        ]
        mock_process.returncode = 1
        mock_process.poll.return_value = 1  # Process has finished
        mock_popen.return_value = mock_process
        
        dashboard._run_test_suite("Test Category", ["pytest", "test_file.py"], verbose=False)
        
        assert len(dashboard.test_results) == 1
        result = dashboard.test_results[0]
        assert result.category == "Test Category"
        assert result.total == 5
        assert result.passed == 3
        assert result.failed == 2
    
    @patch('scripts.dashboard.subprocess.Popen')
    def test_run_test_suite_error(self, mock_popen):
        """Test running a test suite with an error."""
        dashboard = AITestDashboard()
        
        # Mock pytest run that raises an exception
        mock_popen.side_effect = Exception("Test execution failed")
        
        dashboard._run_test_suite("Test Category", ["pytest", "test_file.py"], verbose=False)
        
        assert len(dashboard.test_results) == 1
        result = dashboard.test_results[0]
        assert result.category == "Test Category"
        assert result.total == 0
        assert result.failed == 1  # Error counts as failure
    
    def test_generate_module_support_matrix(self):
        """Test module support matrix generation."""
        dashboard = AITestDashboard()
        
        # Mock the core functionality tests
        dashboard._test_text_generation = MagicMock()
        dashboard._test_image_generation = MagicMock()
        dashboard._test_file_upload = MagicMock()
        dashboard._test_file_download = MagicMock()
        dashboard._test_document_ai = MagicMock()
        dashboard._test_audio_transcription = MagicMock()
        dashboard._test_audio_generation = MagicMock()
        
        dashboard._generate_module_support_matrix()
        
        assert len(dashboard.module_support) > 0
        
        # Check that core features are marked as supported
        supported_features = [m for m in dashboard.module_support if m.status == "✅"]
        assert len(supported_features) >= 5  # At least text, image, file, document, async
    
    def test_dashboard_with_api_key(self):
        """Test dashboard behavior when API key is present."""
        dashboard = AITestDashboard()
        
        # Set API key in environment
        os.environ['AI_API_KEY'] = 'test-api-key'
        
        try:
            with patch.object(dashboard, '_run_test_suite') as mock_run:
                with patch.object(dashboard, '_test_core_functionality'):
                    with patch.object(dashboard, '_test_async_operations'):
                        with patch.object(dashboard, '_generate_module_support_matrix'):
                            dashboard.run_tests(include_integration=True)
            
            # Should have called integration tests when API key is present
            assert mock_run.call_count >= 2  # Unit tests + integration tests
            
        finally:
            os.environ.pop('AI_API_KEY', None)
    
    def test_dashboard_without_api_key(self):
        """Test dashboard behavior when API key is missing."""
        dashboard = AITestDashboard()
        
        # Ensure no API key in environment
        api_key = os.environ.pop('AI_API_KEY', None)
        
        try:
            with patch.object(dashboard, '_run_test_suite') as mock_run:
                with patch.object(dashboard, '_test_core_functionality'):
                    with patch.object(dashboard, '_test_async_operations'):
                        with patch.object(dashboard, '_generate_module_support_matrix'):
                            dashboard.run_tests(include_integration=True)
            
            # Should have skipped integration tests when no API key
            assert mock_run.call_count >= 1  # Unit tests only
            
        finally:
            if api_key:
                os.environ['AI_API_KEY'] = api_key
    
    def test_full_suite_mode(self):
        """Test dashboard in full suite mode."""
        dashboard = AITestDashboard()
        
        with patch.object(dashboard, '_run_chunked_test_suite') as mock_chunked:
            with patch.object(dashboard, '_test_core_functionality'):
                with patch.object(dashboard, '_test_async_operations'):
                    with patch.object(dashboard, '_generate_module_support_matrix'):
                        dashboard.run_tests(full_suite=True)
        
        # Should call _run_chunked_test_suite for complete unit tests
        mock_chunked.assert_called_once()
        call_args = mock_chunked.call_args[0]
        assert "Complete Unit Tests (All Tests)" in call_args[0]
    
    def test_files_api_focus_mode(self):
        """Test dashboard in Files API focus mode."""
        dashboard = AITestDashboard()
        
        with patch.object(dashboard, '_run_test_suite') as mock_run:
            with patch.object(dashboard, '_test_core_functionality'):
                with patch.object(dashboard, '_test_async_operations'):
                    with patch.object(dashboard, '_generate_module_support_matrix'):
                        dashboard.run_tests(full_suite=False)
        
        # Should call run_test_suite for Files API tests
        calls = [call[0][0] for call in mock_run.call_args_list]
        assert any("Files API Unit Tests" in call for call in calls)


@pytest.mark.dashboard
class TestTestDashboardIntegration:
    """Integration tests for the test dashboard."""
    
    def test_dashboard_detects_missing_tests(self):
        """Test that dashboard can detect when test files are missing."""
        dashboard = AITestDashboard()
        
        # This test ensures the dashboard would notice if test files were missing
        # We can't easily test the actual file discovery without modifying the dashboard
        # But we can test the parsing logic
        
        # Test with output indicating missing tests
        output = "collected 0 items\n\n0 passed in 0.1s\n"
        passed, failed, skipped, errors = dashboard._parse_pytest_output(output)
        
        assert passed == 0
        assert failed == 0
    
    def test_dashboard_counts_are_accurate(self):
        """Test that dashboard test counts match actual pytest results."""
        dashboard = AITestDashboard()
        
        # Test various output formats to ensure accurate parsing
        test_cases = [
            ("24 passed in 2.45s", (24, 0, 0, 0)),
            ("10 passed, 5 failed in 3.1s", (10, 5, 0, 0)),
            ("8 passed, 2 failed, 3 skipped in 2.0s", (8, 2, 3, 0)),
        ]
        
        for output, expected in test_cases:
            passed, failed, skipped, errors = dashboard._parse_pytest_output(output)
            assert (passed, failed, skipped, errors) == expected
