<?php

namespace FleekDash\Integrations\Core;

defined('ABSPATH') || exit;

/**
 * High-performance event dispatcher for integrations
 */
class EventDispatcher
{
    private array $listeners = [];
    private array $wildcardListeners = [];
    private array $sorted = [];
    private bool $isDispatching = false;
    private array $eventQueue = [];

    /**
     * Register an event listener
     */
    public function listen(string $event, callable $listener, int $priority = 0): void
    {
        $this->listeners[$event][$priority][] = $listener;
        unset($this->sorted[$event]);
    }

    /**
     * Register a wildcard listener
     */
    public function listenWildcard(string $pattern, callable $listener, int $priority = 0): void
    {
        $this->wildcardListeners[$pattern][$priority][] = $listener;
    }

    /**
     * Dispatch an event
     */
    public function dispatch(string $event, array $payload = [], bool $halt = false)
    {
        if ($this->isDispatching) {
            $this->eventQueue[] = [$event, $payload, $halt];
            return null;
        }

        $this->isDispatching = true;
        $responses = [];

        try {
            $listeners = $this->getListeners($event);

            foreach ($listeners as $listener) {
                $response = $this->callListener($listener, $event, $payload);

                if ($halt && $response !== null) {
                    return $response;
                }

                $responses[] = $response;
            }

            // Process wildcard listeners
            foreach ($this->wildcardListeners as $pattern => $priorityListeners) {
                if ($this->matchesPattern($event, $pattern)) {
                    foreach ($this->sortListeners($priorityListeners) as $listener) {
                        $response = $this->callListener($listener, $event, $payload);

                        if ($halt && $response !== null) {
                            return $response;
                        }

                        $responses[] = $response;
                    }
                }
            }

        } finally {
            $this->isDispatching = false;
            $this->processQueue();
        }

        return $halt ? null : $responses;
    }

    /**
     * Dispatch an event that expects a single response
     */
    public function until(string $event, array $payload = [])
    {
        return $this->dispatch($event, $payload, true);
    }

    /**
     * Get all listeners for an event
     */
    public function getListeners(string $event): array
    {
        if (!isset($this->listeners[$event])) {
            return [];
        }

        if (!isset($this->sorted[$event])) {
            $this->sorted[$event] = $this->sortListeners($this->listeners[$event]);
        }

        return $this->sorted[$event];
    }

    /**
     * Check if an event has listeners
     */
    public function hasListeners(string $event): bool
    {
        return !empty($this->listeners[$event]) || $this->hasWildcardListeners($event);
    }

    /**
     * Remove all listeners for an event
     */
    public function forget(string $event): void
    {
        unset($this->listeners[$event], $this->sorted[$event]);
    }

    /**
     * Remove a specific listener
     */
    public function forgetListener(string $event, callable $listener): void
    {
        if (!isset($this->listeners[$event])) {
            return;
        }

        foreach ($this->listeners[$event] as $priority => $listeners) {
            foreach ($listeners as $key => $existingListener) {
                if ($existingListener === $listener) {
                    unset($this->listeners[$event][$priority][$key]);
                    unset($this->sorted[$event]);
                    break 2;
                }
            }
        }
    }

    /**
     * Clear all listeners
     */
    public function flush(): void
    {
        $this->listeners = [];
        $this->wildcardListeners = [];
        $this->sorted = [];
    }

    /**
     * Sort listeners by priority
     */
    private function sortListeners(array $listeners): array
    {
        krsort($listeners);
        return array_merge(...$listeners);
    }

    /**
     * Call a listener safely
     */
    private function callListener(callable $listener, string $event, array $payload)
    {
        try {
            return call_user_func_array($listener, [$event, $payload]);
        } catch (\Throwable $e) {
            error_log("Event listener error for {$event}: " . $e->getMessage());

            // Optionally fire an error event
            if ($event !== 'integration.listener.error') {
                $this->dispatch('integration.listener.error', [
                    'original_event' => $event,
                    'error' => $e,
                    'listener' => $listener
                ]);
            }

            return null;
        }
    }

    /**
     * Check if event matches wildcard pattern
     */
    private function matchesPattern(string $event, string $pattern): bool
    {
        return fnmatch($pattern, $event);
    }

    /**
     * Check if event has wildcard listeners
     */
    private function hasWildcardListeners(string $event): bool
    {
        foreach ($this->wildcardListeners as $pattern => $listeners) {
            if ($this->matchesPattern($event, $pattern)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Process queued events
     */
    private function processQueue(): void
    {
        while (!empty($this->eventQueue)) {
            [$event, $payload, $halt] = array_shift($this->eventQueue);
            $this->dispatch($event, $payload, $halt);
        }
    }

    /**
     * Register common integration events
     */
    public function registerIntegrationEvents(): void
    {
        // Integration lifecycle events
        $this->listen('integration.discovered', [$this, 'onIntegrationDiscovered']);
        $this->listen('integration.loaded', [$this, 'onIntegrationLoaded']);
        $this->listen('integration.activated', [$this, 'onIntegrationActivated']);
        $this->listen('integration.deactivated', [$this, 'onIntegrationDeactivated']);
        $this->listen('integration.error', [$this, 'onIntegrationError']);

        // Asset events
        $this->listen('integration.assets.enqueue', [$this, 'onAssetsEnqueue']);

        // API events
        $this->listen('integration.api.request', [$this, 'onApiRequest']);
        $this->listen('integration.api.response', [$this, 'onApiResponse']);
    }

    /**
     * Event handlers
     */
    public function onIntegrationDiscovered(string $event, array $payload): void
    {
        $cache = Container::getInstance()->make(Cache::class);
        $cache->cacheIntegrationMeta($payload['id'], $payload['meta']);
    }

    public function onIntegrationLoaded(string $event, array $payload): void
    {
        error_log("Integration loaded: {$payload['id']}");
    }

    public function onIntegrationActivated(string $event, array $payload): void
    {
        $cache = Container::getInstance()->make(Cache::class);
        $cache->delete("integration_status_{$payload['id']}");
    }

    public function onIntegrationDeactivated(string $event, array $payload): void
    {
        $cache = Container::getInstance()->make(Cache::class);
        $cache->delete("integration_status_{$payload['id']}");
    }

    public function onIntegrationError(string $event, array $payload): void
    {
        error_log("Integration error in {$payload['integration']}: {$payload['error']}");
    }

    public function onAssetsEnqueue(string $event, array $payload): void
    {
        // Handle asset enqueuing optimizations
    }

     // Log API requests for debugging
    // public function onApiRequest(string $event, array $payload): void
    // {
    //     if (defined('WP_DEBUG') && WP_DEBUG) {
    //         error_log("API Request: {$payload['endpoint']}");
    //     }
    // }

    public function onApiResponse(string $event, array $payload): void
    {
        // Handle API response caching if applicable
    }
}
