import pytest
from event_registry import EventRegistry, register_event, get_applicable_events

def dummy_event_young(player, mode):
    """Event for young players"""
    return {"type": "test", "message": "young event"}

def dummy_event_old(player, mode):
    """Event for old players"""
    return {"type": "test", "message": "old event"}

def dummy_event_any(player, mode):
    """Event for any player"""
    return {"type": "test", "message": "any event"}

def test_event_registration():
    """Test registering events with conditions"""
    registry = EventRegistry()

    registry.register(
        "test_young",
        dummy_event_young,
        age_range=(0, 18)
    )

    registry.register(
        "test_old",
        dummy_event_old,
        age_range=(65, 120)
    )

    assert registry.count() == 2

def test_get_applicable_events_by_age():
    """Test filtering events by age"""
    registry = EventRegistry()

    registry.register("young", dummy_event_young, age_range=(0, 18))
    registry.register("old", dummy_event_old, age_range=(65, 120))
    registry.register("any", dummy_event_any)  # No conditions

    # Mock player
    class MockPlayer:
        class MockChar:
            ageYears = 10
        c = MockChar()
        events = []

    young_player = MockPlayer()
    young_player.c.ageYears = 10

    applicable = registry.get_applicable_events(young_player)

    # Should get "young" and "any", but not "old"
    assert len(applicable) == 2
    assert "young" in [e['id'] for e in applicable]
    assert "any" in [e['id'] for e in applicable]
    assert "old" not in [e['id'] for e in applicable]

def test_skip_already_triggered_events():
    """Test that already-triggered events are skipped"""
    registry = EventRegistry()

    registry.register("event1", dummy_event_any)
    registry.register("event2", dummy_event_any)

    class MockPlayer:
        class MockChar:
            ageYears = 10
        c = MockChar()
        events = ["event1"]  # Already triggered

    player = MockPlayer()
    applicable = registry.get_applicable_events(player)

    # Should only get event2
    assert len(applicable) == 1
    assert applicable[0]['id'] == "event2"

def test_performance_with_many_events():
    """Test O(1) filtering vs O(n) scanning"""
    import time

    registry = EventRegistry()

    # Register 100 events with age ranges
    for i in range(100):
        age_start = i
        age_end = i + 10
        registry.register(
            f"event_{i}",
            dummy_event_any,
            age_range=(age_start, age_end)
        )

    class MockPlayer:
        class MockChar:
            ageYears = 50
        c = MockChar()
        events = []

    player = MockPlayer()

    # Time 1000 lookups
    start = time.time()
    for _ in range(1000):
        applicable = registry.get_applicable_events(player)
    elapsed = time.time() - start

    # With indexing, should be < 50ms for 1000 lookups
    assert elapsed < 0.05, f"Event filtering too slow: {elapsed}s"

def test_duplicate_registration_prevention():
    """Test that duplicate event registrations are silently skipped"""
    registry = EventRegistry()

    # Register event first time
    registry.register("duplicate_test", dummy_event_any, age_range=(0, 100))
    assert registry.count() == 1

    # Try to register same event again (should be skipped)
    registry.register("duplicate_test", dummy_event_any, age_range=(0, 100))
    assert registry.count() == 1  # Count should not increase

    # Register different event (should succeed)
    registry.register("unique_test", dummy_event_any, age_range=(0, 100))
    assert registry.count() == 2

    # Verify both events are in the registry
    event_ids = registry.list_events()
    assert "duplicate_test" in event_ids
    assert "unique_test" in event_ids

def test_benchmark_old_vs_new():
    """Compare O(n) scanning vs O(1) registry lookup"""
    import time

    # Create dummy event functions to simulate real overhead
    event_funcs = []
    for i in range(85):
        def make_event(idx):
            def event_func(player, mode):
                # Simulate condition checking overhead
                age = player.c.ageYears
                has_events = len(player.events) > 0
                return None if f"event_{idx}" in player.events else {"type": "test"}
            return event_func
        event_funcs.append(make_event(i))

    # Simulate old approach - check all 85 events
    def old_approach(player):
        results = []
        for func in event_funcs:
            result = func(player, 'check')
            if result:
                results.append(result)
        return results

    # New approach - registry with indexing
    registry = EventRegistry()
    for i in range(85):
        age_start = i % 10 * 10
        age_end = age_start + 10
        registry.register(f"event_{i}", event_funcs[i], age_range=(age_start, age_end))

    class MockPlayer:
        class MockChar:
            ageYears = 25
        c = MockChar()
        events = []

    player = MockPlayer()

    # Benchmark old (check all 85)
    start = time.time()
    for _ in range(1000):
        old_approach(player)
    old_time = time.time() - start

    # Benchmark new (check ~8-10 filtered)
    start = time.time()
    for _ in range(1000):
        applicable = registry.get_applicable_events(player)
    new_time = time.time() - start

    print(f"Old (85 checks): {old_time*1000:.1f}ms")
    print(f"New (filtered): {new_time*1000:.1f}ms")
    print(f"Speedup: {old_time/new_time:.1f}x")

    # Should be at least 5x faster
    assert new_time < old_time / 5
