import pytest
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime, timedelta
from threading import Thread
from app.scheduler import Scheduler

def test_scheduler_initialization():
    """Test that Scheduler initializes correctly."""
    # Create a mock app
    mock_app = Mock()
    
    # Initialize scheduler
    scheduler = Scheduler(mock_app, interval_minutes=10)
    
    # Verify initialization
    assert scheduler.app == mock_app
    assert scheduler.interval == 600  # 10 minutes in seconds
    assert scheduler.thread is None
    assert scheduler.running is False

def test_scheduler_init_app():
    """Test that init_app method works correctly."""
    # Create a mock app
    mock_app = Mock()
    mock_app.extensions = {}
    
    # Initialize scheduler
    scheduler = Scheduler()
    scheduler.init_app(mock_app)
    
    # Verify scheduler is stored in app extensions
    assert 'scheduler' in mock_app.extensions
    assert mock_app.extensions['scheduler'] == scheduler

def test_scheduler_start_stop():
    """Test that scheduler can be started and stopped."""
    # Create a mock app
    mock_app = Mock()
    mock_app.app_context.return_value.__enter__.return_value = None
    mock_app.app_context.return_value.__exit__.return_value = None
    
    # Initialize scheduler
    scheduler = Scheduler(mock_app)
    
    # Start the scheduler
    with patch('app.scheduler.Scheduler._run') as mock_run:
        mock_run.side_effect = lambda: setattr(scheduler, 'running', False)  # Stop after one iteration
        
        scheduler.start()
        
        # Give it a moment to start
        import time
        time.sleep(0.1)
        
        # Verify thread was created and started
        assert scheduler.thread is not None
        assert scheduler.running is True
        
        # Wait for the run method to complete
        if scheduler.thread:
            scheduler.thread.join(timeout=1)
        
        # Stop should be called automatically when running becomes False
        assert scheduler.running is False

def test_scheduler_process_all_users_no_users():
    """Test process_all_users with no users in database."""
    # Create a mock app
    mock_app = Mock()
    mock_app.app_context.return_value.__enter__.return_value = None
    mock_app.app_context.return_value.__exit__.return_value = None
    
    # Initialize scheduler
    scheduler = Scheduler(mock_app)
    
    # Mock the User query
    with patch('app.scheduler.User') as mock_user:
        mock_user.query.all.return_value = []
        
        # Call process_all_users
        with patch('app.scheduler.Scheduler.logger') as mock_logger:
            scheduler.process_all_users()
            
            # Verify logger was called
            mock_logger.info.assert_any_call("No users found for processing")

def test_scheduler_process_all_users_with_users():
    """Test process_all_users with users in database."""
    # Create a mock app
    mock_app = Mock()
    mock_app.app_context.return_value.__enter__.return_value = None
    mock_app.app_context.return_value.__exit__.return_value = None
    
    # Initialize scheduler
    scheduler = Scheduler(mock_app)
    
    # Create mock users
    mock_user1 = Mock()
    mock_user1.id = 1
    mock_user1.email = 'user1@example.com'
    
    # Mock the User query
    with patch('app.scheduler.User') as mock_user_class:
        mock_user_class.query.all.return_value = [mock_user1]
        
        # Mock the EmailProcessor
        with patch('app.scheduler.EmailProcessor') as mock_processor:
            mock_processor_instance = mock_processor.return_value
            mock_processor_instance.process_user_emails.return_value = {
                'success_count': 5,
                'error_count': 0,
                'processed_folders': []
            }
            
            # Call process_all_users
            with patch('app.scheduler.Scheduler.logger') as mock_logger:
                scheduler.process_all_users()
                
                # Verify processor was called
                mock_processor.assert_called_once_with(mock_user1)
                mock_processor_instance.process_user_emails.assert_called_once()
                
                # Verify logging
                mock_logger.info.assert_any_call("Processing emails for 1 users")
                mock_logger.info.assert_any_call(
                    f"Completed processing for user {mock_user1.email}: 5 success, 0 errors, 0 folders processed"
                )