"""
Test script for Reports Synchronization functionality
Tests the ReportsSyncResponseHandler with offline support and retry mechanisms
"""

import sys
import os
import json
import tempfile
import shutil
from pathlib import Path
from datetime import datetime, timedelta
from unittest.mock import Mock, MagicMock, patch

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

from mbetterclient.api_client.client import ReportsSyncResponseHandler, APIEndpoint
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.config.manager import ConfigManager
from mbetterclient.config.settings import ApiConfig


def test_reports_sync_handler_initialization():
    """Test 1: ReportsSyncResponseHandler initialization"""
    print("\n=== Test 1: ReportsSyncResponseHandler Initialization ===")
    
    # Create temporary directory for testing
    with tempfile.TemporaryDirectory() as temp_dir:
        # Mock dependencies
        mock_db_manager = Mock()
        mock_api_client = Mock()
        mock_message_bus = Mock()
        
        # Create handler
        handler = ReportsSyncResponseHandler(
            db_manager=mock_db_manager,
            user_data_dir=temp_dir,
            api_client=mock_api_client,
            message_bus=mock_message_bus
        )
        
        # Verify initialization
        assert handler.sync_queue_dir.exists(), "Sync queue directory should be created"
        # Note: sync_queue_file is created on first save, not during 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"✓ Sync queue directory: {handler.sync_queue_dir}")
        print(f"✓ Sync queue file path: {handler.sync_queue_file}")
        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 temp_dir:
        handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        # Generate multiple sync IDs
        sync_ids = [handler._generate_sync_id() for _ in range(5)]
        
        # 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) > 20, f"Sync ID should be long enough: {sync_id}"
        
        # Verify uniqueness
        assert len(set(sync_ids)) == 5, "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 temp_dir:
        # Test with rustdesk_id
        mock_api_client_with_id = Mock()
        mock_settings = Mock()
        mock_settings.rustdesk_id = "test_rustdesk_123"
        mock_api_client_with_id.settings = mock_settings
        
        handler_with_id = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=mock_api_client_with_id,
            message_bus=Mock()
        )
        
        client_id_with_rustdesk = handler_with_id._get_client_id()
        assert client_id_with_rustdesk == "test_rustdesk_123", \
            f"Client ID should use rustdesk_id when available: {client_id_with_rustdesk}"
        
        print("✓ Client ID generated from rustdesk_id")
        print(f"✓ Client ID: {client_id_with_rustdesk}")
        
        # Test without rustdesk_id
        mock_api_client_without_id = Mock()
        mock_settings_no_id = Mock()
        mock_settings_no_id.rustdesk_id = None
        mock_api_client_without_id.settings = mock_settings_no_id
        
        handler_without_id = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=mock_api_client_without_id,
            message_bus=Mock()
        )
        
        client_id_without_rustdesk = handler_without_id._get_client_id()
        assert len(client_id_without_rustdesk) == 16, \
            f"Client ID should be 16 chars without rustdesk_id: {client_id_without_rustdesk}"
        
        print("✓ Client ID generated from machine ID (fallback)")
        print(f"✓ Client ID: {client_id_without_rustdesk}")


def test_sync_queue_operations():
    """Test 4: Sync queue operations"""
    print("\n=== Test 4: Sync Queue Operations ===")
    
    with tempfile.TemporaryDirectory() as temp_dir:
        handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        # Test queue status
        status = handler.get_queue_status()
        assert status['total'] == 0, "Queue should be empty initially"
        assert status['pending'] == 0, "No pending items initially"
        assert status['syncing'] == 0, "No syncing items initially"
        assert status['completed'] == 0, "No completed items initially"
        assert status['failed'] == 0, "No failed items initially"
        
        print("✓ Queue status retrieved successfully")
        print(f"✓ Initial queue status: {status}")
        
        # Test adding items to queue
        test_data = {
            'sync_id': 'test_sync_001',
            'data': {'test': 'data'},
            'queued_at': datetime.utcnow().isoformat(),
            'retry_count': 0,
            'last_attempt': None,
            'status': 'pending'
        }
        
        handler.sync_queue.append(test_data)
        handler._save_sync_queue()
        
        # Verify queue persistence
        new_handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        assert len(new_handler.sync_queue) == 1, "Queue should persist across handler instances"
        assert new_handler.sync_queue[0]['sync_id'] == 'test_sync_001', \
            "Queue data should persist correctly"
        
        print("✓ Queue persistence verified")
        print(f"✓ Queue size after adding item: {len(new_handler.sync_queue)}")


def test_backoff_calculation():
    """Test 5: Exponential backoff calculation"""
    print("\n=== Test 5: Exponential Backoff Calculation ===")
    
    with tempfile.TemporaryDirectory() as temp_dir:
        handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        # Test backoff times for different retry counts
        base = handler.retry_backoff_base
        expected_backoffs = [
            base * (2 ** 0),  # 60s
            base * (2 ** 1),  # 120s
            base * (2 ** 2),  # 240s
            base * (2 ** 3),  # 480s
            base * (2 ** 4),  # 960s
        ]
        
        print("✓ Backoff times calculated:")
        for i, expected in enumerate(expected_backoffs):
            actual = base * (2 ** i)
            assert actual == expected, f"Backoff calculation incorrect for retry {i}"
            print(f"  Retry {i}: {actual}s (expected: {expected}s)")


def test_queue_size_limit():
    """Test 6: Queue size limit enforcement"""
    print("\n=== Test 6: Queue Size Limit Enforcement ===")
    
    with tempfile.TemporaryDirectory() as temp_dir:
        handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        # Add more items than max_queue_size
        for i in range(1100):
            handler.sync_queue.append({
                'sync_id': f'test_sync_{i:04d}',
                'data': {'test': 'data'},
                'queued_at': datetime.utcnow().isoformat(),
                'retry_count': 0,
                'last_attempt': None,
                'status': 'pending'
            })
        
        # Save queue (should enforce size limit)
        handler._save_sync_queue()
        
        # Reload queue to verify limit
        new_handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        assert len(new_handler.sync_queue) <= handler.max_queue_size, \
            f"Queue should not exceed max size: {len(new_handler.sync_queue)} > {handler.max_queue_size}"
        
        print(f"✓ Queue size limit enforced")
        print(f"✓ Max queue size: {handler.max_queue_size}")
        print(f"✓ Actual queue size after limit: {len(new_handler.sync_queue)}")


def test_response_handling():
    """Test 7: Response handling"""
    print("\n=== Test 7: Response Handling ===")
    
    with tempfile.TemporaryDirectory() as temp_dir:
        handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        # Mock endpoint
        mock_endpoint = Mock()
        mock_endpoint.name = "reports_sync"
        
        # Test success response
        mock_response = Mock()
        mock_response.json.return_value = {
            'success': True,
            'synced_count': 10,
            'message': 'Sync successful'
        }
        
        result = handler.handle_response(mock_endpoint, mock_response)
        
        assert result['sync_status'] == 'success', "Sync status should be success"
        assert result['synced_items'] == 10, "Synced items count should match"
        assert result['failed_items'] == 0, "Failed items should be 0"
        
        print("✓ Success response handled correctly")
        print(f"✓ Result: {result}")
        
        # Test error response
        mock_error_response = Mock()
        mock_error_response.json.return_value = {
            'success': False,
            'error': 'Invalid data'
        }
        
        error_result = handler.handle_response(mock_endpoint, mock_error_response)
        
        assert error_result['sync_status'] == 'failed', "Sync status should be failed"
        assert 'Invalid data' in error_result['errors'], "Error should be in errors list"
        
        print("✓ Error response handled correctly")
        print(f"✓ Error result: {error_result}")


def test_error_handling():
    """Test 8: Error handling and retry queuing"""
    print("\n=== Test 8: Error Handling and Retry Queuing ===")
    
    with tempfile.TemporaryDirectory() as temp_dir:
        handler = ReportsSyncResponseHandler(
            db_manager=Mock(),
            user_data_dir=temp_dir,
            api_client=Mock(),
            message_bus=Mock()
        )
        
        # Mock endpoint
        mock_endpoint = Mock()
        mock_endpoint.name = "reports_sync"
        
        # Test error handling
        test_error = Exception("Connection timeout")
        error_result = handler.handle_error(mock_endpoint, test_error)
        
        assert error_result['sync_status'] == 'error', "Sync status should be error"
        assert 'Connection timeout' in error_result['error'], "Error message should be present"
        
        # Verify item was queued for retry
        assert len(handler.sync_queue) > 0, "Error should queue item for retry"
        
        print("✓ Error handled correctly")
        print(f"✓ Error result: {error_result}")
        print(f"✓ Queue size after error: {len(handler.sync_queue)}")


def run_all_tests():
    """Run all tests"""
    print("=" * 80)
    print("REPORTS SYNCHRONIZATION TEST SUITE")
    print("=" * 80)
    
    tests = [
        test_reports_sync_handler_initialization,
        test_sync_id_generation,
        test_client_id_generation,
        test_sync_queue_operations,
        test_backoff_calculation,
        test_queue_size_limit,
        test_response_handling,
        test_error_handling
    ]
    
    passed = 0
    failed = 0
    
    for test_func in tests:
        try:
            test_func()
            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)