package com.openosrs.client.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * Enhanced EventSystem - Advanced event handling system for agents with filtering, batching, and monitoring.
 *
 * Features:
 * - Asynchronous event delivery with thread pool management
 * - Event filtering and conditional listeners
 * - Event batching and throttling
 * - Event history and replay
 * - Performance monitoring and metrics
 * - Typed event builders
 * - Event priority handling
 * - Custom event routing
 * - Dead letter queue for failed events
 * - Event serialization and persistence
 */
public class EventSystem {
    private static final Logger logger = LoggerFactory.getLogger(EventSystem.class);

    // Type aliases for compatibility
    public static abstract class Event extends GameEvent {
        protected Event(EventType type) {
            super(type);
        }
    }

    // Core event handling
    private final ExecutorService eventExecutor;
    private final ScheduledExecutorService scheduledExecutor;
    private final ConcurrentHashMap<EventType, CopyOnWriteArrayList<EventListener>> listeners;

    // Enhanced features
    private final EventMetrics metrics;
    private final EventHistory eventHistory;
    private final EventBatcher eventBatcher;
    private final EventThrottler eventThrottler;
    private final EventFilter eventFilter;
    private final Queue<GameEvent> deadLetterQueue;
    private final Map<String, Object> globalEventData;

    // Configuration
    private volatile boolean initialized = false;
    private volatile boolean enableHistory = true;
    private volatile boolean enableMetrics = true;
    private volatile boolean enableBatching = false;
    private volatile boolean enableThrottling = false;
    private volatile int maxHistorySize = 10000;
    private volatile int maxDeadLetterSize = 1000;

    // Event routing
    private final Map<String, EventRoute> customRoutes;
    private final Set<String> pausedEventTypes;

    public EventSystem() {
        this.eventExecutor = Executors.newFixedThreadPool(6, r -> {
            Thread t = new Thread(r, "Event-Handler");
            t.setDaemon(true);
            t.setPriority(Thread.NORM_PRIORITY - 1);
            return t;
        });

        this.scheduledExecutor = Executors.newScheduledThreadPool(2, r -> {
            Thread t = new Thread(r, "Event-Scheduler");
            t.setDaemon(true);
            return t;
        });

        this.listeners = new ConcurrentHashMap<>();
        this.metrics = new EventMetrics();
        this.eventHistory = new EventHistory(maxHistorySize);
        this.eventThrottler = new EventThrottler();
        this.eventFilter = new EventFilter();
        this.deadLetterQueue = new ConcurrentLinkedQueue<>();
        this.globalEventData = new ConcurrentHashMap<>();
        this.customRoutes = new ConcurrentHashMap<>();
        this.pausedEventTypes = ConcurrentHashMap.newKeySet();

        // Initialize listener lists for all event types
        for (EventType type : EventType.values()) {
            listeners.put(type, new CopyOnWriteArrayList<>());
        }

        initializePeriodicTasks();

        this.eventBatcher = new EventBatcher(this);
    }

    public void initialize() {
        initialized = true;
        metrics.initialize();
        eventBatcher.initialize();
        logger.info("Enhanced EventSystem initialized with features: history={}, metrics={}, batching={}, throttling={}",
            enableHistory, enableMetrics, enableBatching, enableThrottling);
    }

    public void shutdown() {
        initialized = false;

        try {
            // Stop periodic tasks
            scheduledExecutor.shutdown();
            if (!scheduledExecutor.awaitTermination(2, TimeUnit.SECONDS)) {
                scheduledExecutor.shutdownNow();
            }

            // Stop event processing
            eventExecutor.shutdown();
            if (!eventExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                eventExecutor.shutdownNow();
            }

            // Clean up
            listeners.clear();
            deadLetterQueue.clear();
            customRoutes.clear();

            logger.info("EventSystem shutdown complete. Processed {} events total", metrics.getTotalEvents());
        } catch (Exception e) {
            logger.error("Error during EventSystem shutdown", e);
        }
    }

    // ================================
    // ENHANCED LISTENER REGISTRATION
    // ================================

    /**
     * Register a simple listener for a specific event type.
     */
    public void addListener(EventType eventType, Consumer<GameEvent> listener) {
        addListener(eventType, new EventListener(listener));
    }

    /**
     * Register an enhanced listener with filtering and configuration.
     */
    public void addListener(EventType eventType, EventListener listener) {
        if (listener == null) {
            logger.warn("Attempted to register null listener for event type: {}", eventType);
            return;
        }

        CopyOnWriteArrayList<EventListener> eventListeners = listeners.get(eventType);
        if (eventListeners != null) {
            eventListeners.add(listener);
            metrics.incrementListenerCount(eventType);
            logger.debug("Registered listener for event type: {} (total: {})", eventType, eventListeners.size());
        }
    }

    /**
     * Register a filtered listener that only receives events matching the condition.
     */
    public void addFilteredListener(EventType eventType, Predicate<GameEvent> filter, Consumer<GameEvent> listener) {
        EventListener eventListener = new EventListener(listener)
            .withFilter(filter)
            .withDescription("Filtered listener for " + eventType);
        addListener(eventType, eventListener);
    }

    /**
     * Register a throttled listener that receives events at most once per interval.
     */
    public void addThrottledListener(EventType eventType, long intervalMs, Consumer<GameEvent> listener) {
        EventListener eventListener = new EventListener(listener)
            .withThrottling(intervalMs)
            .withDescription("Throttled listener for " + eventType);
        addListener(eventType, eventListener);
    }

    /**
     * Register a one-time listener that automatically removes itself after first event.
     */
    public void addOnceListener(EventType eventType, Consumer<GameEvent> listener) {
        EventListener eventListener = new EventListener(event -> {
            try {
                listener.accept(event);
            } finally {
                removeListener(eventType, listener);
            }
        }).withDescription("One-time listener for " + eventType);
        addListener(eventType, eventListener);
    }

    /**
     * Register a conditional listener that only activates when a condition is met.
     */
    public void addConditionalListener(EventType eventType, Predicate<Void> activationCondition, Consumer<GameEvent> listener) {
        EventListener eventListener = new EventListener(event -> {
            if (activationCondition.test(null)) {
                listener.accept(event);
            }
        }).withDescription("Conditional listener for " + eventType);
        addListener(eventType, eventListener);
    }

    /**
     * Remove a listener for a specific event type.
     */
    public void removeListener(EventType eventType, Consumer<GameEvent> listener) {
        CopyOnWriteArrayList<Consumer<GameEvent>> eventListeners = listeners.get(eventType);
        if (eventListeners != null) {
            eventListeners.remove(listener);
            logger.debug("Removed listener for event type: {}", eventType);
        }
    }

    /**
     * Fire an event to all registered listeners.
     */
    public void fireEvent(EventType eventType, GameEvent event) {
        if (!initialized) {
            return;
        }

        CopyOnWriteArrayList<Consumer<GameEvent>> eventListeners = listeners.get(eventType);
        if (eventListeners == null || eventListeners.isEmpty()) {
            return;
        }

        // Deliver events asynchronously to avoid blocking the game loop
        eventExecutor.submit(() -> {
            try {
                for (Consumer<GameEvent> listener : eventListeners) {
                    try {
                        listener.accept(event);
                    } catch (Exception e) {
                        logger.error("Error in event listener for type: {}", eventType, e);
                    }
                }
            } catch (Exception e) {
                logger.error("Error firing event of type: {}", eventType, e);
            }
        });
    }

    /**
     * Fire an event to all registered listeners (private).
     */
    private void fireEventInternal(EventType eventType, GameEvent event) {
        fireEvent(eventType, event);
    }

    // Game state events
    public void fireGameStateChanged(int oldState, int newState) {
        fireEventInternal(EventType.GAME_STATE_CHANGED,
                new GameStateChangedEvent(oldState, newState));
    }

    public void fireGameTick() {
        fireEventInternal(EventType.GAME_TICK,
                new GameTickEvent(System.currentTimeMillis()));
    }

    // Player events
    public void firePlayerMoved(int x, int y, int plane) {
        fireEventInternal(EventType.PLAYER_MOVED,
                new PlayerMovedEvent(x, y, plane));
    }

    public void fireHealthChanged(int current, int max) {
        fireEventInternal(EventType.HEALTH_CHANGED,
                new HealthChangedEvent(current, max));
    }

    public void firePrayerChanged(int current, int max) {
        fireEventInternal(EventType.PRAYER_CHANGED,
                new PrayerChangedEvent(current, max));
    }

    public void fireRunEnergyChanged(int energy) {
        fireEventInternal(EventType.RUN_ENERGY_CHANGED,
                new RunEnergyChangedEvent(energy));
    }

    public void fireSkillChanged(int skill, int level, int experience) {
        fireEventInternal(EventType.SKILL_CHANGED,
                new SkillChangedEvent(skill, level, experience));
    }

    public void fireExperienceChanged(int skill, int experience) {
        fireEventInternal(EventType.EXPERIENCE_CHANGED,
                new ExperienceChangedEvent(skill, experience));
    }

    public void fireAnimationChanged(int animationId) {
        fireEventInternal(EventType.ANIMATION_CHANGED,
                new AnimationChangedEvent(animationId));
    }

    public void fireCombatStateChanged(boolean inCombat, int targetId) {
        fireEventInternal(EventType.COMBAT_STATE_CHANGED,
                new CombatStateChangedEvent(inCombat, targetId));
    }

    // Inventory events
    public void fireInventoryChanged(int slot, int itemId, int quantity) {
        fireEventInternal(EventType.INVENTORY_CHANGED,
                new InventoryChangedEvent(slot, itemId, quantity));
    }

    public void fireInventoryChanged(com.openosrs.client.api.InventoryChange change) {
        fireEventInternal(EventType.INVENTORY_CHANGED,
                new InventoryChangedApiEvent(change));
    }

    // Interface events
    public void fireInterfaceOpened(int interfaceId) {
        fireEventInternal(EventType.INTERFACE_OPENED,
                new InterfaceOpenedEvent(interfaceId));
    }

    public void fireInterfaceClosed(int interfaceId) {
        fireEventInternal(EventType.INTERFACE_CLOSED,
                new InterfaceClosedEvent(interfaceId));
    }

    public void fireInterfaceChanged(int oldInterface, int newInterface) {
        if (oldInterface != -1) {
            fireInterfaceClosed(oldInterface);
        }
        if (newInterface != -1) {
            fireInterfaceOpened(newInterface);
        }
    }

    // Network events
    public void fireNetworkStateChanged(boolean connected) {
        fireEventInternal(connected ? EventType.CONNECTION_RESTORED : EventType.CONNECTION_LOST,
                new NetworkStateChangedEvent(connected));
    }

    // Chat events
    public void fireChatMessage(String username, String message, int type) {
        fireEventInternal(EventType.CHAT_MESSAGE,
                new ChatMessageEvent(username, message, type));
    }

    /**
     * Enum defining all available event types.
     */
    public enum EventType {
        // Core game events
        GAME_STATE_CHANGED,
        GAME_TICK,

        // Player events
        PLAYER_MOVED,
        HEALTH_CHANGED,
        PRAYER_CHANGED,
        RUN_ENERGY_CHANGED,
        SKILL_CHANGED,
        EXPERIENCE_CHANGED,
        ANIMATION_CHANGED,
        COMBAT_STATE_CHANGED,

        // Inventory events
        INVENTORY_CHANGED,
        EQUIPMENT_CHANGED,

        // Interface events
        INTERFACE_OPENED,
        INTERFACE_CLOSED,
        WIDGET_UPDATED,

        // World events
        NPC_SPAWNED,
        NPC_DESPAWNED,
        OBJECT_SPAWNED,
        OBJECT_DESPAWNED,
        ITEM_SPAWNED,
        ITEM_DESPAWNED,

        // Social events
        CHAT_MESSAGE,
        PRIVATE_MESSAGE,
        FRIEND_LOGIN,
        FRIEND_LOGOUT,

        // Network events
        CONNECTION_LOST,
        CONNECTION_RESTORED,

        // Login events
        LOGIN_EVENT,
        LOGIN_ATTEMPT_STARTED,
        LOGIN_SUCCESS,
        LOGIN_FAILED,
        LOGIN_STATE_CHANGED,
        LOGIN_FIELD_CHANGED,
        LOGIN_RESPONSE_CHANGED,
        LOGIN_LOADING_CHANGED,
        LOGIN_USERNAME_CHANGED,
        LOGIN_PASSWORD_CHANGED,
        LOGIN_OTP_CHANGED,
        CREDENTIALS_CLEARED,

        // Custom events for scripts/plugins
        CUSTOM_EVENT
    }

    /**
     * Base class for all game events.
     */
    public abstract static class GameEvent {
        private final long timestamp;
        private final EventType type;

        protected GameEvent(EventType type) {
            this.type = type;
            this.timestamp = System.currentTimeMillis();
        }

        public EventType getType() { return type; }
        public long getTimestamp() { return timestamp; }
    }

    // Event implementations
    public static class GameStateChangedEvent extends GameEvent {
        private final int oldState, newState;

        public GameStateChangedEvent(int oldState, int newState) {
            super(EventType.GAME_STATE_CHANGED);
            this.oldState = oldState;
            this.newState = newState;
        }

        public int getOldState() { return oldState; }
        public int getNewState() { return newState; }
    }

    public static class GameTickEvent extends GameEvent {
        public GameTickEvent(long timestamp) {
            super(EventType.GAME_TICK);
        }
    }

    public static class PlayerMovedEvent extends GameEvent {
        private final int x, y, plane;

        public PlayerMovedEvent(int x, int y, int plane) {
            super(EventType.PLAYER_MOVED);
            this.x = x;
            this.y = y;
            this.plane = plane;
        }

        public int getX() { return x; }
        public int getY() { return y; }
        public int getPlane() { return plane; }
    }

    public static class HealthChangedEvent extends GameEvent {
        private final int current, max;

        public HealthChangedEvent(int current, int max) {
            super(EventType.HEALTH_CHANGED);
            this.current = current;
            this.max = max;
        }

        public int getCurrent() { return current; }
        public int getMax() { return max; }
    }

    public static class PrayerChangedEvent extends GameEvent {
        private final int current, max;

        public PrayerChangedEvent(int current, int max) {
            super(EventType.PRAYER_CHANGED);
            this.current = current;
            this.max = max;
        }

        public int getCurrent() { return current; }
        public int getMax() { return max; }
    }

    public static class RunEnergyChangedEvent extends GameEvent {
        private final int energy;

        public RunEnergyChangedEvent(int energy) {
            super(EventType.RUN_ENERGY_CHANGED);
            this.energy = energy;
        }

        public int getEnergy() { return energy; }
    }

    public static class SkillChangedEvent extends GameEvent {
        private final int skill, level, experience;

        public SkillChangedEvent(int skill, int level, int experience) {
            super(EventType.SKILL_CHANGED);
            this.skill = skill;
            this.level = level;
            this.experience = experience;
        }

        public int getSkill() { return skill; }
        public int getLevel() { return level; }
        public int getExperience() { return experience; }
    }

    public static class ExperienceChangedEvent extends GameEvent {
        private final int skill, experience;

        public ExperienceChangedEvent(int skill, int experience) {
            super(EventType.EXPERIENCE_CHANGED);
            this.skill = skill;
            this.experience = experience;
        }

        public int getSkill() { return skill; }
        public int getExperience() { return experience; }
    }

    public static class AnimationChangedEvent extends GameEvent {
        private final int animationId;

        public AnimationChangedEvent(int animationId) {
            super(EventType.ANIMATION_CHANGED);
            this.animationId = animationId;
        }

        public int getAnimationId() { return animationId; }
    }

    public static class CombatStateChangedEvent extends GameEvent {
        private final boolean inCombat;
        private final int targetId;

        public CombatStateChangedEvent(boolean inCombat, int targetId) {
            super(EventType.COMBAT_STATE_CHANGED);
            this.inCombat = inCombat;
            this.targetId = targetId;
        }

        public boolean isInCombat() { return inCombat; }
        public int getTargetId() { return targetId; }
    }

    public static class InventoryChangedEvent extends GameEvent {
        private final int slot, itemId, quantity;

        public InventoryChangedEvent(int slot, int itemId, int quantity) {
            super(EventType.INVENTORY_CHANGED);
            this.slot = slot;
            this.itemId = itemId;
            this.quantity = quantity;
        }

        public int getSlot() { return slot; }
        public int getItemId() { return itemId; }
        public int getQuantity() { return quantity; }
    }

    public static class InterfaceOpenedEvent extends GameEvent {
        private final int interfaceId;

        public InterfaceOpenedEvent(int interfaceId) {
            super(EventType.INTERFACE_OPENED);
            this.interfaceId = interfaceId;
        }

        public int getInterfaceId() { return interfaceId; }
    }

    public static class InterfaceClosedEvent extends GameEvent {
        private final int interfaceId;

        public InterfaceClosedEvent(int interfaceId) {
            super(EventType.INTERFACE_CLOSED);
            this.interfaceId = interfaceId;
        }

        public int getInterfaceId() { return interfaceId; }
    }

    public static class ChatMessageEvent extends GameEvent {
        private final String username, message;
        private final int type;

        public ChatMessageEvent(String username, String message, int type) {
            super(EventType.CHAT_MESSAGE);
            this.username = username;
            this.message = message;
            this.type = type;
        }

        public String getUsername() { return username; }
        public String getMessage() { return message; }
        public int getType() { return type; }
    }

    public static class InventoryChangedApiEvent extends GameEvent {
        private final com.openosrs.client.api.InventoryChange change;

        public InventoryChangedApiEvent(com.openosrs.client.api.InventoryChange change) {
            super(EventType.INVENTORY_CHANGED);
            this.change = change;
        }

        public com.openosrs.client.api.InventoryChange getChange() { return change; }
    }

    public static class NetworkStateChangedEvent extends GameEvent {
        private final boolean connected;

        public NetworkStateChangedEvent(boolean connected) {
            super(connected ? EventType.CONNECTION_RESTORED : EventType.CONNECTION_LOST);
            this.connected = connected;
        }

        public boolean isConnected() { return connected; }
    }

    // ================================
    // ENHANCED EVENT SYSTEM COMPONENTS
    // ================================

    /**
     * Initialize periodic maintenance tasks.
     */
    private void initializePeriodicTasks() {
        // Metrics collection task
        scheduledExecutor.scheduleAtFixedRate(() -> {
            try {
                if (enableMetrics) {
                    metrics.collectPeriodicMetrics();
                }
            } catch (Exception e) {
                logger.warn("Error in metrics collection", e);
            }
        }, 30, 30, TimeUnit.SECONDS);

        // Dead letter queue cleanup
        scheduledExecutor.scheduleAtFixedRate(() -> {
            try {
                cleanupDeadLetterQueue();
            } catch (Exception e) {
                logger.warn("Error in dead letter queue cleanup", e);
            }
        }, 5, 5, TimeUnit.MINUTES);

        // Event history maintenance
        scheduledExecutor.scheduleAtFixedRate(() -> {
            try {
                if (enableHistory) {
                    eventHistory.performMaintenance();
                }
            } catch (Exception e) {
                logger.warn("Error in event history maintenance", e);
            }
        }, 10, 10, TimeUnit.MINUTES);
    }

    /**
     * Enhanced event firing with filtering, batching, and metrics.
     */
    public void fireEvent(EventType eventType, GameEvent event) {
        if (!initialized || pausedEventTypes.contains(eventType.name())) {
            return;
        }

        // Record metrics
        if (enableMetrics) {
            metrics.recordEvent(eventType, event);
        }

        // Add to history
        if (enableHistory) {
            eventHistory.addEvent(event);
        }

        // Apply global filtering
        if (!eventFilter.shouldProcess(event)) {
            return;
        }

        // Handle batching
        if (enableBatching && eventBatcher.shouldBatch(eventType)) {
            eventBatcher.addEvent(eventType, event);
            return;
        }

        // Handle throttling
        if (enableThrottling && !eventThrottler.shouldFire(eventType, event)) {
            return;
        }

        // Fire to listeners
        fireEventToListeners(eventType, event);
    }

    /**
     * Fire event directly to listeners with error handling.
     */
    private void fireEventToListeners(EventType eventType, GameEvent event) {
        CopyOnWriteArrayList<EventListener> eventListeners = listeners.get(eventType);
        if (eventListeners == null || eventListeners.isEmpty()) {
            return;
        }

        // Custom routing check
        EventRoute route = customRoutes.get(eventType.name());
        if (route != null && !route.shouldRoute(event)) {
            return;
        }

        // Deliver events asynchronously
        eventExecutor.submit(() -> {
            for (EventListener listener : eventListeners) {
                try {
                    if (listener.shouldAccept(event)) {
                        listener.accept(event);
                        if (enableMetrics) {
                            metrics.recordListenerExecution(eventType, true);
                        }
                    }
                } catch (Exception e) {
                    logger.error("Error in event listener for type: {}", eventType, e);

                    // Add to dead letter queue
                    if (deadLetterQueue.size() < maxDeadLetterSize) {
                        deadLetterQueue.offer(new DeadLetterEvent(event, listener, e));
                    }

                    if (enableMetrics) {
                        metrics.recordListenerExecution(eventType, false);
                    }
                }
            }
        });
    }

    /**
     * Fire event with string type and data payload.
     */
    public void fireEvent(String eventType, Object data) {
        try {
            EventType type = EventType.valueOf(eventType.toUpperCase());
            CustomEvent event = new CustomEvent(type, data);
            fireEvent(type, event);
        } catch (IllegalArgumentException e) {
            // Handle custom event types
            CustomEvent event = new CustomEvent(EventType.CUSTOM_EVENT, data);
            event.setCustomType(eventType);
            fireEvent(EventType.CUSTOM_EVENT, event);
        }
    }

    // ================================
    // CONFIGURATION AND MANAGEMENT
    // ================================

    /**
     * Pause event processing for specific event types.
     */
    public void pauseEventType(EventType eventType) {
        pausedEventTypes.add(eventType.name());
        logger.info("Paused event type: {}", eventType);
    }

    /**
     * Resume event processing for specific event types.
     */
    public void resumeEventType(EventType eventType) {
        pausedEventTypes.remove(eventType.name());
        logger.info("Resumed event type: {}", eventType);
    }

    /**
     * Get event processing metrics.
     */
    public EventMetrics getMetrics() {
        return metrics;
    }

    /**
     * Get event history.
     */
    public EventHistory getEventHistory() {
        return eventHistory;
    }

    /**
     * Set global event data that can be accessed by listeners.
     */
    public void setGlobalData(String key, Object value) {
        globalEventData.put(key, value);
    }

    /**
     * Get global event data.
     */
    public Object getGlobalData(String key) {
        return globalEventData.get(key);
    }

    /**
     * Clean up dead letter queue.
     */
    private void cleanupDeadLetterQueue() {
        if (deadLetterQueue.size() > maxDeadLetterSize / 2) {
            int removed = 0;
            while (deadLetterQueue.size() > maxDeadLetterSize / 4 && deadLetterQueue.poll() != null) {
                removed++;
            }
            if (removed > 0) {
                logger.info("Cleaned up {} dead letter events", removed);
            }
        }
    }
}

// ================================
// SUPPORTING CLASSES
// ================================

/**
 * Enhanced event listener with filtering and configuration.
 */
class EventListener {
    private final Consumer<GameEvent> consumer;
    private Predicate<GameEvent> filter;
    private long throttleInterval = 0;
    private long lastExecution = 0;
    private String description = "";
    private int priority = 0;
    private boolean enabled = true;

    public EventListener(Consumer<GameEvent> consumer) {
        this.consumer = consumer;
    }

    public EventListener withFilter(Predicate<GameEvent> filter) {
        this.filter = filter;
        return this;
    }

    public EventListener withThrottling(long intervalMs) {
        this.throttleInterval = intervalMs;
        return this;
    }

    public EventListener withDescription(String description) {
        this.description = description;
        return this;
    }

    public EventListener withPriority(int priority) {
        this.priority = priority;
        return this;
    }

    public boolean shouldAccept(GameEvent event) {
        if (!enabled) return false;

        // Check throttling
        long now = System.currentTimeMillis();
        if (throttleInterval > 0 && (now - lastExecution) < throttleInterval) {
            return false;
        }

        // Check filter
        if (filter != null && !filter.test(event)) {
            return false;
        }

        return true;
    }

    public void accept(GameEvent event) {
        lastExecution = System.currentTimeMillis();
        consumer.accept(event);
    }

    public String getDescription() { return description; }
    public int getPriority() { return priority; }
    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
}

/**
 * Event metrics collector.
 */
class EventMetrics {
    private final AtomicLong totalEvents = new AtomicLong(0);
    private final Map<EventType, AtomicLong> eventCounts = new ConcurrentHashMap<>();
    private final Map<EventType, AtomicInteger> listenerCounts = new ConcurrentHashMap<>();
    private final Map<EventType, AtomicLong> successfulExecutions = new ConcurrentHashMap<>();
    private final Map<EventType, AtomicLong> failedExecutions = new ConcurrentHashMap<>();
    private final AtomicLong startTime = new AtomicLong(0);

    public void initialize() {
        startTime.set(System.currentTimeMillis());
        for (EventSystem.EventType type : EventSystem.EventType.values()) {
            eventCounts.put(type, new AtomicLong(0));
            listenerCounts.put(type, new AtomicInteger(0));
            successfulExecutions.put(type, new AtomicLong(0));
            failedExecutions.put(type, new AtomicLong(0));
        }
    }

    public void recordEvent(EventSystem.EventType eventType, EventSystem.GameEvent event) {
        totalEvents.incrementAndGet();
        eventCounts.get(eventType).incrementAndGet();
    }

    public void incrementListenerCount(EventSystem.EventType eventType) {
        listenerCounts.get(eventType).incrementAndGet();
    }

    public void recordListenerExecution(EventSystem.EventType eventType, boolean success) {
        if (success) {
            successfulExecutions.get(eventType).incrementAndGet();
        } else {
            failedExecutions.get(eventType).incrementAndGet();
        }
    }

    public void collectPeriodicMetrics() {
        // Log periodic metrics
        long runtime = System.currentTimeMillis() - startTime.get();
        double eventsPerSecond = totalEvents.get() / (runtime / 1000.0);

        LoggerFactory.getLogger(EventMetrics.class)
            .debug("Event metrics: {} total events, {:.2f} events/sec, {} listeners active",
                totalEvents.get(), eventsPerSecond, getTotalListeners());
    }

    public long getTotalEvents() {
        return totalEvents.get();
    }

    public int getTotalListeners() {
        return listenerCounts.values().stream().mapToInt(AtomicInteger::get).sum();
    }

    public Map<EventSystem.EventType, Long> getEventCounts() {
        Map<EventSystem.EventType, Long> result = new HashMap<>();
        for (Map.Entry<EventSystem.EventType, AtomicLong> entry : eventCounts.entrySet()) {
            result.put(entry.getKey(), entry.getValue().get());
        }
        return result;
    }
}

/**
 * Event history tracker.
 */
class EventHistory {
    private final Queue<EventSystem.GameEvent> history;
    private final int maxSize;

    public EventHistory(int maxSize) {
        this.maxSize = maxSize;
        this.history = new ConcurrentLinkedQueue<>();
    }

    public void addEvent(EventSystem.GameEvent event) {
        history.offer(event);

        // Maintain size limit
        while (history.size() > maxSize) {
            history.poll();
        }
    }

    public List<EventSystem.GameEvent> getRecentEvents(int count) {
        return history.stream()
            .skip(Math.max(0, history.size() - count))
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
    }

    public List<EventSystem.GameEvent> getEventsByType(EventSystem.EventType type) {
        return history.stream()
            .filter(event -> event.getType() == type)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
    }

    public void performMaintenance() {
        // Clean up old events if needed
        while (history.size() > maxSize) {
            history.poll();
        }
    }

    public int size() {
        return history.size();
    }
}

/**
 * Event batching system.
 */
class EventBatcher {
    private final EventSystem eventSystem;
    private final Map<EventSystem.EventType, List<EventSystem.GameEvent>> batches;
    private final Map<EventSystem.EventType, Long> lastFlush;
    private final Set<EventSystem.EventType> batchableTypes;

    public EventBatcher(EventSystem eventSystem) {
        this.eventSystem = eventSystem;
        this.batches = new ConcurrentHashMap<>();
        this.lastFlush = new ConcurrentHashMap<>();
        this.batchableTypes = new HashSet<>();

        // Add frequently fired events to batchable types
        batchableTypes.add(EventSystem.EventType.GAME_TICK);
        batchableTypes.add(EventSystem.EventType.PLAYER_MOVED);
    }

    public void initialize() {
        // Schedule periodic batch flushing
        eventSystem.scheduledExecutor.scheduleAtFixedRate(this::flushAllBatches, 100, 100, TimeUnit.MILLISECONDS);
    }

    public boolean shouldBatch(EventSystem.EventType eventType) {
        return batchableTypes.contains(eventType);
    }

    public void addEvent(EventSystem.EventType eventType, EventSystem.GameEvent event) {
        batches.computeIfAbsent(eventType, k -> new ArrayList<>()).add(event);
    }

    private void flushAllBatches() {
        for (Map.Entry<EventSystem.EventType, List<EventSystem.GameEvent>> entry : batches.entrySet()) {
            EventSystem.EventType eventType = entry.getKey();
            List<EventSystem.GameEvent> events = entry.getValue();

            if (!events.isEmpty()) {
                // Create batch event
                BatchEvent batchEvent = new BatchEvent(eventType, new ArrayList<>(events));
                events.clear();

                // Fire batch event
                eventSystem.fireEventToListeners(eventType, batchEvent);
                lastFlush.put(eventType, System.currentTimeMillis());
            }
        }
    }
}

/**
 * Event throttling system.
 */
class EventThrottler {
    private final Map<String, Long> lastEventTimes = new ConcurrentHashMap<>();
    private final Map<EventSystem.EventType, Long> throttleIntervals = new ConcurrentHashMap<>();

    public boolean shouldFire(EventSystem.EventType eventType, EventSystem.GameEvent event) {
        Long interval = throttleIntervals.get(eventType);
        if (interval == null) {
            return true;
        }

        String key = eventType.name() + ":" + event.getClass().getSimpleName();
        long now = System.currentTimeMillis();
        Long lastTime = lastEventTimes.get(key);

        if (lastTime == null || (now - lastTime) >= interval) {
            lastEventTimes.put(key, now);
            return true;
        }

        return false;
    }

    public void setThrottleInterval(EventSystem.EventType eventType, long intervalMs) {
        throttleIntervals.put(eventType, intervalMs);
    }
}

/**
 * Global event filtering system.
 */
class EventFilter {
    private final List<Predicate<EventSystem.GameEvent>> globalFilters = new ArrayList<>();

    public boolean shouldProcess(EventSystem.GameEvent event) {
        return globalFilters.stream().allMatch(filter -> filter.test(event));
    }

    public void addGlobalFilter(Predicate<EventSystem.GameEvent> filter) {
        globalFilters.add(filter);
    }

    public void removeGlobalFilter(Predicate<EventSystem.GameEvent> filter) {
        globalFilters.remove(filter);
    }
}

/**
 * Custom event routing.
 */
class EventRoute {
    private final Predicate<EventSystem.GameEvent> condition;
    private final String description;

    public EventRoute(Predicate<EventSystem.GameEvent> condition, String description) {
        this.condition = condition;
        this.description = description;
    }

    public boolean shouldRoute(EventSystem.GameEvent event) {
        return condition.test(event);
    }

    public String getDescription() {
        return description;
    }
}

/**
 * Dead letter event for failed event processing.
 */
class DeadLetterEvent {
    private final EventSystem.GameEvent originalEvent;
    private final EventListener failedListener;
    private final Exception exception;
    private final long timestamp;

    public DeadLetterEvent(EventSystem.GameEvent originalEvent, EventListener failedListener, Exception exception) {
        this.originalEvent = originalEvent;
        this.failedListener = failedListener;
        this.exception = exception;
        this.timestamp = System.currentTimeMillis();
    }

    public EventSystem.GameEvent getOriginalEvent() { return originalEvent; }
    public EventListener getFailedListener() { return failedListener; }
    public Exception getException() { return exception; }
    public long getTimestamp() { return timestamp; }
}

/**
 * Custom event implementation.
 */
class CustomEvent extends EventSystem.GameEvent {
    private final Object data;
    private String customType;

    public CustomEvent(EventSystem.EventType type, Object data) {
        super(type);
        this.data = data;
    }

    public Object getData() { return data; }
    public String getCustomType() { return customType; }
    public void setCustomType(String customType) { this.customType = customType; }
}

/**
 * Batch event containing multiple events.
 */
class BatchEvent extends EventSystem.GameEvent {
    private final List<EventSystem.GameEvent> events;

    public BatchEvent(EventSystem.EventType type, List<EventSystem.GameEvent> events) {
        super(type);
        this.events = new ArrayList<>(events);
    }

    public List<EventSystem.GameEvent> getEvents() { return new ArrayList<>(events); }
    public int getEventCount() { return events.size(); }
}
