"""
Test suite for ReportsSyncResponseHandler with database-based queue
"""

import sys
import os
import tempfile
import shutil
from datetime import datetime, timedelta
from pathlib import Path

# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from mbetterclient.database.manager import DatabaseManager
from mbetterclient.database.models import ReportsSyncQueueModel
from mbetterclient.api_client.client import ReportsSyncResponseHandler
from mbetterclient.config.manager import ConfigManager
from mbetterclient.config.settings import ApiConfig


def test_handler_initialization():
    """Test 1: ReportsSyncResponseHandler Initialization"""
    print("\n=== Test 1: ReportsSyncResponseHandler Initialization ===")
    
    # Create temporary directory for test
    with tempfile.TemporaryDirectory() as tmpdir:
        # Create database manager
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        # Initialize handler
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir,
            api_client=None,
            message_bus=None
        )
        
        # Verify initialization
        assert handler.max_queue_size == 1000, "Max queue size should be 1000"
        assert handler.max_retries == 5, "Max retries should be 5"
        assert handler.retry_backoff_base == 60, "Retry backoff base should be 60"
        
        print("✓ Handler initialized successfully")
        print(f"✓ Max queue size: {handler.max_queue_size}")
        print(f"✓ Max retries: {handler.max_retries}")
        print(f"✓ Retry backoff base: {handler.retry_backoff_base}s")


def test_sync_id_generation():
    """Test 2: Sync ID Generation"""
    print("\n=== Test 2: Sync ID Generation ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Generate multiple sync IDs
        sync_ids = [handler._generate_sync_id() for _ in range(10)]
        
        # Verify format
        for sync_id in sync_ids:
            assert sync_id.startswith("sync_"), f"Sync ID should start with 'sync_': {sync_id}"
            assert len(sync_id) > 10, f"Sync ID should be long enough: {sync_id}"
        
        # Verify uniqueness
        assert len(sync_ids) == len(set(sync_ids)), "All sync IDs should be unique"
        
        print("✓ Sync IDs generated successfully")
        print(f"✓ Sample sync ID: {sync_ids[0]}")
        print(f"✓ All sync IDs are unique")


def test_client_id_generation():
    """Test 3: Client ID Generation"""
    print("\n=== Test 3: Client ID Generation ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Test with rustdesk_id
        class MockSettings:
            rustdesk_id = "test_rustdesk_123"
        
        handler.api_client = type('obj', (object,), {'settings': MockSettings()})()
        client_id = handler._get_client_id()
        assert client_id == "test_rustdesk_123", f"Client ID should match rustdesk_id: {client_id}"
        print(f"✓ Client ID generated from rustdesk_id")
        print(f"✓ Client ID: {client_id}")
        
        # Test fallback to machine ID
        handler.api_client = type('obj', (object,), {'settings': type('obj', (object,), {})()})()
        client_id = handler._get_client_id()
        assert len(client_id) == 16, f"Client ID should be 16 characters: {client_id}"
        print(f"✓ Client ID generated from machine ID (fallback)")
        print(f"✓ Client ID: {client_id}")


def test_sync_queue_operations():
    """Test 4: Sync Queue Operations"""
    print("\n=== Test 4: Sync Queue Operations ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Get initial queue status
        status = handler.get_queue_status()
        print(f"✓ Queue status retrieved successfully")
        print(f"✓ Initial queue status: {status}")
        
        # Add item to queue
        sync_data = {
            'sync_id': 'test_sync_001',
            'client_id': 'test_client',
            'timestamp': datetime.utcnow().isoformat(),
            'bets': [],
            'extraction_stats': []
        }
        
        session = db_manager.get_session()
        try:
            queue_item = ReportsSyncQueueModel(
                sync_id='test_sync_001',
                client_id='test_client',
                status='pending',
                retry_count=0,
                sync_data=sync_data,
                synced_items=0,
                failed_items=0
            )
            session.add(queue_item)
            session.commit()
        finally:
            session.close()
        
        # Verify queue persistence
        status = handler.get_queue_status()
        assert status['total'] == 1, f"Queue should have 1 item: {status}"
        assert status['pending'] == 1, f"Queue should have 1 pending item: {status}"
        print(f"✓ Queue persistence verified")
        print(f"✓ Queue size after adding item: {status['total']}")


def test_exponential_backoff_calculation():
    """Test 5: Exponential Backoff Calculation"""
    print("\n=== Test 5: Exponential Backoff Calculation ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Calculate backoff times
        backoff_times = []
        for retry_count in range(5):
            backoff_time = handler._calculate_backoff_time(retry_count)
            backoff_times.append(backoff_time)
        
        # Verify exponential backoff
        expected_times = [60, 120, 240, 480, 960]
        assert backoff_times == expected_times, f"Backoff times should match expected: {backoff_times}"
        
        print("✓ Backoff times calculated:")
        for i, (actual, expected) in enumerate(zip(backoff_times, expected_times)):
            print(f"  Retry {i}: {actual}s (expected: {expected}s)")


def test_queue_size_limit_enforcement():
    """Test 6: Queue Size Limit Enforcement"""
    print("\n=== Test 6: Queue Size Limit Enforcement ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Set small queue size for testing
        handler.max_queue_size = 10
        
        # Add items up to limit
        session = db_manager.get_session()
        try:
            for i in range(10):
                sync_data = {
                    'sync_id': f'test_sync_{i:03d}',
                    'client_id': 'test_client',
                    'timestamp': datetime.utcnow().isoformat(),
                    'bets': [],
                    'extraction_stats': []
                }
                
                queue_item = ReportsSyncQueueModel(
                    sync_id=f'test_sync_{i:03d}',
                    client_id='test_client',
                    status='pending',
                    retry_count=0,
                    sync_data=sync_data,
                    synced_items=0,
                    failed_items=0
                )
                session.add(queue_item)
            session.commit()
        finally:
            session.close()
        
        # Verify queue size at limit (pending items)
        status = handler.get_queue_status()
        assert status['pending'] == handler.max_queue_size, f"Queue should have {handler.max_queue_size} pending items: {status}"
        
        # Mark some items as completed
        session = db_manager.get_session()
        try:
            completed_items = session.query(ReportsSyncQueueModel).filter_by(status='pending').limit(5).all()
            for item in completed_items:
                item.mark_completed(0, 0)
            session.commit()
        finally:
            session.close()
        
        # Verify pending count decreased
        status = handler.get_queue_status()
        assert status['pending'] == 5, f"Queue should have 5 pending items after marking 5 as completed: {status}"
        assert status['completed'] == 5, f"Queue should have 5 completed items: {status}"
        
        print("✓ Queue size limit configuration verified")
        print(f"✓ Max queue size: {handler.max_queue_size}")
        print(f"✓ Pending items: {status['pending']}")
        print(f"✓ Completed items: {status['completed']}")


def test_response_handling():
    """Test 7: Response Handling"""
    print("\n=== Test 7: Response Handling ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Test success response
        class MockResponse:
            def json(self):
                return {
                    'success': True,
                    'synced_count': 10,
                    'failed_count': 0
                }
        
        class MockEndpoint:
            name = 'reports_sync'
        
        response = MockResponse()
        endpoint = MockEndpoint()
        result = handler.handle_response(endpoint, response)
        
        assert result['sync_status'] == 'success', f"Sync status should be success: {result}"
        assert result['synced_items'] == 10, f"Synced items should be 10: {result}"
        print("✓ Success response handled correctly")
        print(f"✓ Result: {result}")
        
        # Test error response
        class MockErrorResponse:
            def json(self):
                return {
                    'success': False,
                    'error': 'Invalid data'
                }
        
        error_response = MockErrorResponse()
        result = handler.handle_response(endpoint, error_response)
        
        assert result['sync_status'] == 'failed', f"Sync status should be failed: {result}"
        assert 'Invalid data' in result['errors'], f"Error should be in errors: {result}"
        print("✓ Error response handled correctly")
        print(f"✓ Error result: {result}")


def test_error_handling_and_retry_queuing():
    """Test 8: Error Handling and Retry Queuing"""
    print("\n=== Test 8: Error Handling and Retry Queuing ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        handler = ReportsSyncResponseHandler(
            db_manager=db_manager,
            user_data_dir=tmpdir
        )
        
        # Test error handling
        class MockEndpoint:
            name = 'reports_sync'
        
        error = Exception("Connection timeout")
        endpoint = MockEndpoint()
        result = handler.handle_error(endpoint, error)
        
        assert result['sync_status'] == 'error', f"Sync status should be error: {result}"
        assert 'Connection timeout' in result['error'], f"Error message should be present: {result}"
        print("✓ Error handled correctly")
        print(f"✓ Error result: {result}")
        
        # Verify item was queued
        status = handler.get_queue_status()
        assert status['total'] > 0, f"Queue should have items after error: {status}"
        print(f"✓ Queue size after error: {status['total']}")


def test_database_model_methods():
    """Test 9: Database Model Methods"""
    print("\n=== Test 9: Database Model Methods ===")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        db_manager = DatabaseManager(db_path=os.path.join(tmpdir, "test.db"))
        db_manager.initialize()
        
        session = db_manager.get_session()
        try:
            # Create queue item
            sync_data = {
                'sync_id': 'test_sync_001',
                'client_id': 'test_client',
                'timestamp': datetime.utcnow().isoformat(),
                'bets': [],
                'extraction_stats': []
            }
            
            queue_item = ReportsSyncQueueModel(
                sync_id='test_sync_001',
                client_id='test_client',
                status='pending',
                retry_count=0,
                sync_data=sync_data,
                synced_items=0,
                failed_items=0
            )
            session.add(queue_item)
            session.commit()
            
            # Test status methods
            assert queue_item.is_pending(), "Item should be pending"
            assert not queue_item.is_syncing(), "Item should not be syncing"
            assert not queue_item.is_completed(), "Item should not be completed"
            assert not queue_item.is_failed(), "Item should not be failed"
            print("✓ Status methods work correctly")
            
            # Test retry methods
            assert queue_item.can_retry(5), "Item should be able to retry"
            assert queue_item.should_retry_now(), "Item should retry now"
            print("✓ Retry methods work correctly")
            
            # Test mark methods
            queue_item.mark_syncing()
            session.commit()
            assert queue_item.is_syncing(), "Item should be syncing"
            print("✓ mark_syncing() works correctly")
            
            queue_item.mark_completed(10, 0)
            session.commit()
            assert queue_item.is_completed(), "Item should be completed"
            assert queue_item.synced_items == 10, "Synced items should be 10"
            print("✓ mark_completed() works correctly")
            
            # Create new item for testing mark_failed
            queue_item2 = ReportsSyncQueueModel(
                sync_id='test_sync_002',
                client_id='test_client',
                status='pending',
                retry_count=0,
                sync_data=sync_data,
                synced_items=0,
                failed_items=0
            )
            session.add(queue_item2)
            session.commit()
            
            next_retry = datetime.utcnow() + timedelta(seconds=60)
            queue_item2.mark_failed("Test error", 1, next_retry)
            session.commit()
            assert queue_item2.status == 'pending', "Status should be pending for retry"
            assert queue_item2.retry_count == 1, "Retry count should be 1"
            assert queue_item2.error_message == "Test error", "Error message should be set"
            print("✓ mark_failed() works correctly")
            
        finally:
            session.close()


def run_all_tests():
    """Run all tests"""
    print("=" * 80)
    print("REPORTS SYNCHRONIZATION TEST SUITE (DATABASE-BASED QUEUE)")
    print("=" * 80)
    
    tests = [
        test_handler_initialization,
        test_sync_id_generation,
        test_client_id_generation,
        test_sync_queue_operations,
        test_exponential_backoff_calculation,
        test_queue_size_limit_enforcement,
        test_response_handling,
        test_error_handling_and_retry_queuing,
        test_database_model_methods,
    ]
    
    passed = 0
    failed = 0
    
    for test in tests:
        try:
            test()
            passed += 1
        except AssertionError as e:
            print(f"\n✗ Test failed: {e}")
            failed += 1
        except Exception as e:
            print(f"\n✗ Test error: {e}")
            import traceback
            traceback.print_exc()
            failed += 1
    
    print("\n" + "=" * 80)
    print("TEST SUMMARY")
    print("=" * 80)
    print(f"Total tests: {len(tests)}")
    print(f"Passed: {passed}")
    print(f"Failed: {failed}")
    print(f"Success rate: {(passed/len(tests)*100):.1f}%")
    print("=" * 80)
    
    return failed == 0


if __name__ == "__main__":
    success = run_all_tests()
    sys.exit(0 if success else 1)