"""Unit tests for media scan startup logic."""

from unittest.mock import AsyncMock, Mock, patch

import pytest

from src.server.database.system_settings_service import SystemSettingsService


class TestMediaScanStartup:
    """Test media scan startup behavior."""

    @pytest.mark.asyncio
    async def test_media_scan_runs_on_first_startup(self):
        """Test that media scan runs when flag is False."""
        # Mock SystemSettingsService to return False for media scan completed
        with patch.object(
            SystemSettingsService,
            'is_initial_media_scan_completed',
            new_callable=AsyncMock
        ) as mock_is_completed:
            mock_is_completed.return_value = False
            
            with patch.object(
                SystemSettingsService,
                'mark_initial_media_scan_completed',
                new_callable=AsyncMock
            ) as mock_mark_completed:
                
                # Mock the check incomplete series function
                mock_check_incomplete = AsyncMock()
                
                # Simulate the startup logic
                is_media_scan_done = await SystemSettingsService.is_initial_media_scan_completed(None)
                
                assert is_media_scan_done is False
                
                # If not done, the startup should call check_incomplete_series
                if not is_media_scan_done:
                    await mock_check_incomplete()
                    await SystemSettingsService.mark_initial_media_scan_completed(None)
                
                # Verify check_incomplete was called
                mock_check_incomplete.assert_called_once()
                
                # Verify mark_completed was called
                mock_mark_completed.assert_called_once()

    @pytest.mark.asyncio
    async def test_media_scan_skipped_on_subsequent_startup(self):
        """Test that media scan is skipped when flag is True."""
        # Mock SystemSettingsService to return True for media scan completed
        with patch.object(
            SystemSettingsService,
            'is_initial_media_scan_completed',
            new_callable=AsyncMock
        ) as mock_is_completed:
            mock_is_completed.return_value = True
            
            with patch.object(
                SystemSettingsService,
                'mark_initial_media_scan_completed',
                new_callable=AsyncMock
            ) as mock_mark_completed:
                
                # Mock the check incomplete series function
                mock_check_incomplete = AsyncMock()
                
                # Simulate the startup logic
                is_media_scan_done = await SystemSettingsService.is_initial_media_scan_completed(None)
                
                assert is_media_scan_done is True
                
                # If done, the startup should NOT call check_incomplete_series
                if not is_media_scan_done:
                    await mock_check_incomplete()
                    await SystemSettingsService.mark_initial_media_scan_completed(None)
                
                # Verify check_incomplete was NOT called
                mock_check_incomplete.assert_not_called()
                
                # Verify mark_completed was NOT called
                mock_mark_completed.assert_not_called()

    @pytest.mark.asyncio
    async def test_media_scan_handles_check_error_gracefully(self):
        """Test that startup handles errors when checking media scan status."""
        # Mock SystemSettingsService to raise an exception
        with patch.object(
            SystemSettingsService,
            'is_initial_media_scan_completed',
            new_callable=AsyncMock
        ) as mock_is_completed:
            mock_is_completed.side_effect = Exception("Database error")
            
            # Simulate the startup logic with error handling
            is_media_scan_done = False
            try:
                is_media_scan_done = await SystemSettingsService.is_initial_media_scan_completed(None)
            except Exception:
                # In case of error, assume not done
                is_media_scan_done = False
            
            # Should default to False (not done) on error
            assert is_media_scan_done is False
            
            # Verify the exception was raised
            mock_is_completed.assert_called_once()

    @pytest.mark.asyncio
    async def test_media_scan_handles_mark_error_gracefully(self):
        """Test that startup handles errors when marking media scan complete."""
        with patch.object(
            SystemSettingsService,
            'is_initial_media_scan_completed',
            new_callable=AsyncMock
        ) as mock_is_completed:
            mock_is_completed.return_value = False
            
            with patch.object(
                SystemSettingsService,
                'mark_initial_media_scan_completed',
                new_callable=AsyncMock
            ) as mock_mark_completed:
                # Make mark_completed raise an exception
                mock_mark_completed.side_effect = Exception("Database write error")
                
                mock_check_incomplete = AsyncMock()
                
                # Simulate the startup logic with error handling
                is_media_scan_done = await SystemSettingsService.is_initial_media_scan_completed(None)
                
                if not is_media_scan_done:
                    await mock_check_incomplete()
                    
                    # Try to mark as completed, but handle error
                    try:
                        await SystemSettingsService.mark_initial_media_scan_completed(None)
                    except Exception:
                        # Error should be caught and logged, but not crash startup
                        pass
                
                # Verify check_incomplete was still called
                mock_check_incomplete.assert_called_once()
                
                # Verify mark_completed was attempted
                mock_mark_completed.assert_called_once()

    @pytest.mark.asyncio
    async def test_check_incomplete_series_integration(self):
        """Test the _check_incomplete_series_on_startup function behavior."""
        from src.server.database.models import AnimeSeries

        # Mock database session
        mock_db = AsyncMock()
        
        # Create mock series with incomplete data
        mock_series_incomplete = Mock(spec=AnimeSeries)
        mock_series_incomplete.key = "incomplete-series"
        mock_series_incomplete.name = "Incomplete Series"
        mock_series_incomplete.folder = "Incomplete Series"
        mock_series_incomplete.year = 2020
        mock_series_incomplete.loading_status = "pending"
        mock_series_incomplete.episodes_loaded = False
        mock_series_incomplete.has_nfo = False
        mock_series_incomplete.logo_loaded = False
        mock_series_incomplete.images_loaded = False
        
        # Create mock series with complete data
        mock_series_complete = Mock(spec=AnimeSeries)
        mock_series_complete.key = "complete-series"
        mock_series_complete.loading_status = "completed"
        mock_series_complete.episodes_loaded = True
        mock_series_complete.has_nfo = True
        mock_series_complete.logo_loaded = True
        mock_series_complete.images_loaded = True
        
        # Mock AnimeSeriesService.get_all to return both series
        with patch('src.server.database.service.AnimeSeriesService.get_all') as mock_get_all:
            mock_get_all.return_value = [mock_series_incomplete, mock_series_complete]
            
            # Mock background loader
            mock_background_loader = Mock()
            mock_background_loader.add_series_loading_task = AsyncMock()
            
            # Import and call the function
            from src.server.fastapi_app import _check_incomplete_series_on_startup

            # Mock get_db_session (it's imported inside the function)
            with patch('src.server.database.connection.get_db_session') as mock_get_db:
                mock_get_db.return_value.__aenter__.return_value = mock_db
                
                await _check_incomplete_series_on_startup(mock_background_loader)
            
            # Verify only incomplete series was queued
            mock_background_loader.add_series_loading_task.assert_called_once_with(
                key="incomplete-series",
                folder="Incomplete Series",
                name="Incomplete Series",
                year=2020
            )

    @pytest.mark.asyncio
    async def test_check_incomplete_series_all_complete(self):
        """Test behavior when all series have complete data."""
        from src.server.database.models import AnimeSeries
        
        mock_db = AsyncMock()
        
        # Create mock series with complete data
        mock_series = Mock(spec=AnimeSeries)
        mock_series.key = "complete-series"
        mock_series.loading_status = "completed"
        mock_series.episodes_loaded = True
        mock_series.has_nfo = True
        mock_series.logo_loaded = True
        mock_series.images_loaded = True
        
        with patch('src.server.database.service.AnimeSeriesService.get_all') as mock_get_all:
            mock_get_all.return_value = [mock_series]
            
            mock_background_loader = Mock()
            mock_background_loader.add_series_loading_task = AsyncMock()
            
            from src.server.fastapi_app import _check_incomplete_series_on_startup
            
            with patch('src.server.database.connection.get_db_session') as mock_get_db:
                mock_get_db.return_value.__aenter__.return_value = mock_db
                
                await _check_incomplete_series_on_startup(mock_background_loader)
            
            # Verify no series were queued
            mock_background_loader.add_series_loading_task.assert_not_called()

    @pytest.mark.asyncio
    async def test_check_incomplete_series_missing_episodes(self):
        """Test that series with missing episodes are queued."""
        from src.server.database.models import AnimeSeries
        
        mock_db = AsyncMock()
        
        # Series marked complete but missing episodes
        mock_series = Mock(spec=AnimeSeries)
        mock_series.key = "series-no-episodes"
        mock_series.name = "Series Without Episodes"
        mock_series.folder = "Series Without Episodes"
        mock_series.year = 2021
        mock_series.loading_status = "completed"
        mock_series.episodes_loaded = False  # Missing
        mock_series.has_nfo = True
        mock_series.logo_loaded = True
        mock_series.images_loaded = True
        
        with patch('src.server.database.service.AnimeSeriesService.get_all') as mock_get_all:
            mock_get_all.return_value = [mock_series]
            
            mock_background_loader = Mock()
            mock_background_loader.add_series_loading_task = AsyncMock()
            
            from src.server.fastapi_app import _check_incomplete_series_on_startup
            
            with patch('src.server.database.connection.get_db_session') as mock_get_db:
                mock_get_db.return_value.__aenter__.return_value = mock_db
                
                await _check_incomplete_series_on_startup(mock_background_loader)
            
            # Verify series was queued for loading
            mock_background_loader.add_series_loading_task.assert_called_once()

    @pytest.mark.asyncio
    async def test_check_incomplete_series_error_handling(self):
        """Test error handling in check_incomplete_series_on_startup."""
        mock_background_loader = Mock()
        
        # Mock database error
        with patch('src.server.database.connection.get_db_session') as mock_get_db:
            mock_get_db.side_effect = Exception("Database connection error")
            
            from src.server.fastapi_app import _check_incomplete_series_on_startup

            # Should not raise exception
            try:
                await _check_incomplete_series_on_startup(mock_background_loader)
            except Exception as e:
                pytest.fail(f"Function should not raise exception, but raised: {e}")
