#!/usr/bin/env python3
"""
Test script for the Qt6 discovery application
Tests core functionality without requiring a display
"""

import sys
import json
import socket
import threading
import time
from unittest.mock import Mock, patch

def test_imports():
    """Test that all required modules can be imported"""
    print("Testing imports...")
    
    try:
        import json
        print("✓ json module")
        
        import socket
        print("✓ socket module")
        
        import threading
        print("✓ threading module")
        
        import webbrowser
        print("✓ webbrowser module")
        
        from PyQt6.QtWidgets import QApplication
        print("✓ PyQt6.QtWidgets")
        
        from PyQt6.QtCore import QTimer, pyqtSignal, QObject, Qt, QThread
        print("✓ PyQt6.QtCore")
        
        print("✓ All imports successful")
        return True
        
    except ImportError as e:
        print(f"✗ Import failed: {e}")
        return False

def test_udp_worker_class():
    """Test UDPDiscoveryWorker class without GUI"""
    print("\nTesting UDPDiscoveryWorker class...")
    
    try:
        # Import the worker class definition
        import importlib.util
        spec = importlib.util.spec_from_file_location("mbetter_discovery", "mbetter_discovery.py")
        discovery_module = importlib.util.module_from_spec(spec)
        
        # Mock PyQt6 to avoid GUI requirements
        with patch.dict('sys.modules', {
            'PyQt6.QtWidgets': Mock(),
            'PyQt6.QtCore': Mock(QObject=object, pyqtSignal=Mock(), QThread=Mock()),
            'PyQt6.QtGui': Mock()
        }):
            spec.loader.exec_module(discovery_module)
            
            # Test worker class can be instantiated
            worker = discovery_module.UDPDiscoveryWorker(45123)
            print("✓ UDPDiscoveryWorker instantiated")
            
            # Test basic properties
            assert worker.listen_port == 45123
            assert worker.running == False
            print("✓ Worker properties correct")
            
            return True
            
    except Exception as e:
        print(f"✗ UDPDiscoveryWorker test failed: {e}")
        return False

def test_message_processing():
    """Test message processing logic"""
    print("\nTesting message processing...")
    
    try:
        # Test valid MBetterClient message
        valid_message = {
            "service": "MBetterClient",
            "host": "192.168.1.100",
            "port": 5001,
            "ssl": False,
            "url": "http://192.168.1.100:5001",
            "timestamp": time.time()
        }
        
        # Test JSON encoding/decoding (simulates UDP message processing)
        json_data = json.dumps(valid_message)
        decoded_message = json.loads(json_data)
        
        # Validate message structure
        if (isinstance(decoded_message, dict) and 
            decoded_message.get('service') == 'MBetterClient' and
            'url' in decoded_message):
            print("✓ Valid message structure detected")
        else:
            print("✗ Message validation failed")
            return False
        
        # Test invalid message
        invalid_message = {"service": "OtherService", "url": "http://example.com"}
        if not (invalid_message.get('service') == 'MBetterClient'):
            print("✓ Invalid message correctly rejected")
        
        return True
        
    except Exception as e:
        print(f"✗ Message processing test failed: {e}")
        return False

def test_discovery_integration():
    """Test discovery integration with UDP broadcast"""
    print("\nTesting discovery integration...")
    
    def mock_listener():
        """Mock UDP listener that receives broadcasts"""
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.bind(('', 45124))  # Use different port to avoid conflicts
            sock.settimeout(3.0)
            
            print("Mock listener started on port 45124")
            
            data, addr = sock.recvfrom(1024)
            message = json.loads(data.decode('utf-8'))
            
            if message.get('service') == 'MBetterClient':
                print(f"✓ Received test broadcast: {message['url']}")
                sock.close()
                return True
            else:
                print("✗ Invalid broadcast received")
                sock.close()
                return False
                
        except socket.timeout:
            print("⚠ No broadcast received (timeout)")
            sock.close()
            return False
        except Exception as e:
            print(f"✗ Mock listener error: {e}")
            return False
    
    def send_test_broadcast():
        """Send test broadcast to mock listener"""
        time.sleep(0.5)  # Wait for listener to start
        
        try:
            test_message = {
                "service": "MBetterClient",
                "host": "127.0.0.1",
                "port": 5001,
                "ssl": False,
                "url": "http://127.0.0.1:5001",
                "timestamp": time.time()
            }
            
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            
            data = json.dumps(test_message).encode('utf-8')
            sock.sendto(data, ('127.0.0.1', 45124))
            sock.close()
            
            print("✓ Test broadcast sent")
            
        except Exception as e:
            print(f"✗ Failed to send test broadcast: {e}")
    
    # Start mock listener in thread
    listener_thread = threading.Thread(target=mock_listener)
    listener_thread.daemon = True
    listener_thread.start()
    
    # Send test broadcast
    sender_thread = threading.Thread(target=send_test_broadcast)
    sender_thread.daemon = True
    sender_thread.start()
    
    # Wait for completion
    listener_thread.join(timeout=4)
    sender_thread.join(timeout=1)
    
    if listener_thread.is_alive():
        print("⚠ Integration test timed out")
        return False
    
    print("✓ Integration test completed")
    return True

def main():
    """Main test function"""
    print("=" * 60)
    print("Qt6 Discovery Application Test")
    print("=" * 60)
    
    tests = [
        ("Module Imports", test_imports),
        ("UDPDiscoveryWorker", test_udp_worker_class),
        ("Message Processing", test_message_processing),
        ("Discovery Integration", test_discovery_integration),
    ]
    
    results = []
    
    for test_name, test_func in tests:
        print(f"\n--- {test_name} Test ---")
        try:
            result = test_func()
            results.append((test_name, result))
        except Exception as e:
            print(f"✗ {test_name} test crashed: {e}")
            results.append((test_name, False))
    
    # Summary
    print("\n" + "=" * 60)
    print("Test Results Summary")
    print("=" * 60)
    
    passed = 0
    total = len(results)
    
    for test_name, result in results:
        status = "✓ PASS" if result else "✗ FAIL"
        print(f"{test_name:<20} {status}")
        if result:
            passed += 1
    
    print(f"\nOverall: {passed}/{total} tests passed")
    
    if passed == total:
        print("🎉 All tests passed! Discovery application is ready for use.")
        return True
    else:
        print("⚠ Some tests failed. Check the output above for details.")
        return False

if __name__ == "__main__":
    try:
        success = main()
        sys.exit(0 if success else 1)
    except KeyboardInterrupt:
        print("\n\nTest interrupted by user")
        sys.exit(1)