"""
Test Suite: BusMonitorCore ARTOS API Compliance

This test validates that BusMonitorCore implements the ARTOS BaseModule
interface correctly and can be used identically by both GUI and ARTOS Collector.
"""
import time
from pathlib import Path
import sys
import os

# Ensure we can import the module
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if project_root not in sys.path:
    sys.path.insert(0, project_root)

from pybusmonitor1553.core import BusMonitorCore, BaseModule


def test_basemodule_compliance():
    """Test that BusMonitorCore correctly implements BaseModule interface."""
    print("=" * 60)
    print("TEST 1: BaseModule Interface Compliance")
    print("=" * 60)
    
    monitor = BusMonitorCore()
    
    # Check that it's an instance of BaseModule
    assert isinstance(monitor, BaseModule), "BusMonitorCore must inherit from BaseModule"
    print("✓ BusMonitorCore inherits from BaseModule")
    
    # Check required methods exist
    required_methods = ['initialize', 'start_session', 'stop_session', 'get_status']
    for method in required_methods:
        assert hasattr(monitor, method), f"Missing required method: {method}"
        assert callable(getattr(monitor, method)), f"{method} must be callable"
    print(f"✓ All required methods present: {', '.join(required_methods)}")
    
    print("✓ TEST 1 PASSED\n")


def test_initialization_workflow():
    """Test the initialization workflow (same as ARTOS would use)."""
    print("=" * 60)
    print("TEST 2: Initialization Workflow")
    print("=" * 60)
    
    monitor = BusMonitorCore()
    
    # Configure like ARTOS would
    config = {
        'ip': '127.0.0.1',
        'send_port': 5001,
        'recv_port': 5002
    }
    
    print(f"Initializing with config: {config}")
    success = monitor.initialize(config)
    
    if not success:
        status = monitor.get_status()
        print(f"⚠ Initialization failed (expected in test environment): {status.get('errors')}")
        print("This is OK - testing API structure, not actual hardware")
    else:
        print("✓ Initialization successful")
    
    # Check status is accessible
    status = monitor.get_status()
    assert isinstance(status, dict), "get_status() must return a dict"
    assert 'is_running' in status, "Status must include 'is_running'"
    assert 'connected' in status, "Status must include 'connected'"
    assert 'errors' in status, "Status must include 'errors'"
    print(f"✓ Status structure correct: {list(status.keys())}")
    
    print("✓ TEST 2 PASSED\n")


def test_session_lifecycle():
    """Test start/stop session workflow."""
    print("=" * 60)
    print("TEST 3: Session Lifecycle")
    print("=" * 60)
    
    monitor = BusMonitorCore()
    config = {'ip': '127.0.0.1', 'send_port': 5001, 'recv_port': 5002}
    
    # Initialize
    monitor.initialize(config)
    initial_status = monitor.get_status()
    print(f"Initial status: is_running={initial_status['is_running']}")
    
    # Start session
    monitor.start_session()
    running_status = monitor.get_status()
    print(f"After start: is_running={running_status['is_running']}")
    
    # Give it a moment
    time.sleep(0.5)
    
    # Stop session
    monitor.stop_session()
    stopped_status = monitor.get_status()
    print(f"After stop: is_running={stopped_status['is_running']}")
    
    assert stopped_status['is_running'] == False, "Should not be running after stop"
    print("✓ Session lifecycle works correctly")
    
    print("✓ TEST 3 PASSED\n")


def test_message_access():
    """Test message retrieval API."""
    print("=" * 60)
    print("TEST 4: Message Access API")
    print("=" * 60)
    
    monitor = BusMonitorCore()
    config = {'ip': '127.0.0.1', 'send_port': 5001, 'recv_port': 5002}
    monitor.initialize(config)
    
    # Test get_message method exists
    assert hasattr(monitor, 'get_message'), "Must have get_message method"
    assert hasattr(monitor, 'get_all_messages'), "Must have get_all_messages method"
    print("✓ Message access methods present")
    
    # Try to get messages (may be empty in test environment)
    all_messages = monitor.get_all_messages()
    print(f"Messages available: {len(all_messages) if all_messages else 0}")
    
    if all_messages:
        # Try to get a specific message
        first_label = list(all_messages.keys())[0]
        msg = monitor.get_message(first_label)
        print(f"✓ Successfully retrieved message: {first_label}")
    else:
        print("ℹ No messages in test environment (OK for unit test)")
    
    print("✓ TEST 4 PASSED\n")


def test_callback_system():
    """Test callback registration (ARTOS test hooks)."""
    print("=" * 60)
    print("TEST 5: Callback System")
    print("=" * 60)
    
    monitor = BusMonitorCore()
    
    # Check callback registration method exists
    assert hasattr(monitor, 'register_callback'), "Must have register_callback method"
    print("✓ Callback registration method present")
    
    # Register a test callback
    callback_invoked = {'count': 0}
    
    def test_callback(message):
        callback_invoked['count'] += 1
        print(f"  Callback invoked with message: {message}")
    
    monitor.register_callback("test_label", test_callback)
    print("✓ Callback registered successfully")
    
    print("✓ TEST 5 PASSED\n")


def test_recording_api():
    """Test recording/replay API (ARTOS feature)."""
    print("=" * 60)
    print("TEST 6: Recording & Replay API")
    print("=" * 60)
    
    monitor = BusMonitorCore()
    
    # Check recording methods exist
    required_recording_methods = [
        'start_recording',
        'stop_recording',
        'load_recording'
    ]
    
    for method in required_recording_methods:
        assert hasattr(monitor, method), f"Missing recording method: {method}"
        assert callable(getattr(monitor, method)), f"{method} must be callable"
    
    print(f"✓ All recording methods present: {', '.join(required_recording_methods)}")
    
    # Test recording workflow
    test_file = Path("test_recordings/test_session.json")
    test_file.parent.mkdir(exist_ok=True)
    
    monitor.start_recording(filepath=test_file)
    status = monitor.get_status()
    assert status['recording'] == True, "Should be recording"
    print("✓ Recording started")
    
    saved_path = monitor.stop_recording(save=True)
    status = monitor.get_status()
    assert status['recording'] == False, "Should not be recording"
    print("✓ Recording stopped")
    
    if saved_path and saved_path.exists():
        print(f"✓ Recording saved to: {saved_path}")
        # Clean up
        saved_path.unlink()
        saved_path.parent.rmdir()
    
    print("✓ TEST 6 PASSED\n")


def test_same_api_for_gui_and_artos():
    """
    Meta-test: Verify GUI and ARTOS would use identical code.
    
    This is the critical validation - if this passes, GUI is truly
    testing the same API that ARTOS will use.
    """
    print("=" * 60)
    print("TEST 7: API Identity Validation")
    print("=" * 60)
    
    # This is what GUI does:
    from pybusmonitor1553.core import BusMonitorCore as GUIMonitor
    gui_monitor = GUIMonitor()
    
    # This is what ARTOS would do:
    from pybusmonitor1553.core import BusMonitorCore as ARTOSMonitor
    artos_monitor = ARTOSMonitor()
    
    # They should be the same class
    assert type(gui_monitor) == type(artos_monitor), "GUI and ARTOS use different classes!"
    print("✓ GUI and ARTOS import identical BusMonitorCore class")
    
    # Both should have the same methods
    gui_methods = {m for m in dir(gui_monitor) if not m.startswith('_') and callable(getattr(gui_monitor, m))}
    artos_methods = {m for m in dir(artos_monitor) if not m.startswith('_') and callable(getattr(artos_monitor, m))}
    
    assert gui_methods == artos_methods, "GUI and ARTOS have different public methods!"
    print(f"✓ Both expose identical public API: {len(gui_methods)} methods")
    
    # Test that workflow is identical
    config = {'ip': '127.0.0.1', 'send_port': 5001, 'recv_port': 5002}
    
    # GUI workflow
    gui_monitor.initialize(config)
    gui_status = gui_monitor.get_status()
    
    # ARTOS workflow
    artos_monitor.initialize(config)
    artos_status = artos_monitor.get_status()
    
    # Status structures should be identical
    assert gui_status.keys() == artos_status.keys(), "Status structures differ!"
    print("✓ Status structures identical")
    
    print("\n" + "=" * 60)
    print("CRITICAL VALIDATION PASSED!")
    print("=" * 60)
    print("GUI is testing the EXACT API that ARTOS will use.")
    print("If GUI works → ARTOS will work!")
    print("=" * 60)
    
    print("✓ TEST 7 PASSED\n")


def run_all_tests():
    """Run complete test suite."""
    print("\n" + "=" * 60)
    print("ARTOS API COMPLIANCE TEST SUITE")
    print("=" * 60)
    print("Validating that BusMonitorCore implements ARTOS interface\n")
    
    tests = [
        test_basemodule_compliance,
        test_initialization_workflow,
        test_session_lifecycle,
        test_message_access,
        test_callback_system,
        test_recording_api,
        test_same_api_for_gui_and_artos,
    ]
    
    passed = 0
    failed = 0
    
    for test_func in tests:
        try:
            test_func()
            passed += 1
        except AssertionError as e:
            print(f"✗ TEST FAILED: {test_func.__name__}")
            print(f"  Error: {e}\n")
            failed += 1
        except Exception as e:
            print(f"✗ TEST ERROR: {test_func.__name__}")
            print(f"  Exception: {e}\n")
            failed += 1
    
    # Summary
    print("\n" + "=" * 60)
    print("TEST SUITE SUMMARY")
    print("=" * 60)
    print(f"Total tests: {len(tests)}")
    print(f"Passed: {passed} ✓")
    print(f"Failed: {failed} ✗")
    print("=" * 60)
    
    if failed == 0:
        print("\n🎉 ALL TESTS PASSED!")
        print("\nBusMonitorCore is ARTOS-compliant and ready for integration.")
        print("The GUI is now a true test client for the ARTOS module.")
        return 0
    else:
        print("\n⚠ SOME TESTS FAILED")
        print("Please review failures before ARTOS integration.")
        return 1


if __name__ == '__main__':
    exit_code = run_all_tests()
    sys.exit(exit_code)
