<?php
declare(strict_types=1);

namespace App\Core;

use App\Core\Events\Event;

final class EventBus
{
    private static array $listeners = [];
    private static bool $testMode = false;
    private static array $dispatched = [];

    public static function listen(string $event, callable $handler, int $priority = 0): void
    {
        self::$listeners[$event][] = [
            'handler'  => $handler,
            'priority' => $priority,
        ];
    }

    public static function dispatch(string $event, array $data = []): array
    {
        if (self::$testMode) {
            self::$dispatched[] = ['event' => $event, 'data' => $data];
            return [];
        }

        if (!isset(self::$listeners[$event])) {
            return [];
        }

        $sorted = self::$listeners[$event];
        usort($sorted, fn($a, $b) => $b['priority'] <=> $a['priority']);

        $results = [];
        foreach ($sorted as $listener) {
            $result = ($listener['handler'])($data);
            if ($result !== null) {
                $results[] = $result;
            }
        }

        return $results;
    }

    public static function dispatchEvent(Event $event): array
    {
        return self::dispatch($event::eventName(), $event->toArray());
    }

    public static function hasListeners(string $event): bool
    {
        return !empty(self::$listeners[$event]);
    }

    public static function getListeners(string $event): array
    {
        return self::$listeners[$event] ?? [];
    }

    public static function clearListeners(string $event): void
    {
        unset(self::$listeners[$event]);
    }

    public static function enableTestMode(): void
    {
        self::$testMode = true;
        self::$dispatched = [];
    }

    public static function disableTestMode(): void
    {
        self::$testMode = false;
        self::$dispatched = [];
    }

    public static function getDispatched(): array
    {
        return self::$dispatched;
    }

    public static function dispatchAsync(string $event, array $data = []): void
    {
        try {
            $db = App::getInstance()->db();
            $db->insert('async_event_queue', [
                'event_name'     => $event,
                'event_data_json' => json_encode($data, JSON_UNESCAPED_UNICODE),
                'status'         => 'pending',
                'attempts'       => 0,
                'max_attempts'   => 3,
                'created_at'     => date('Y-m-d H:i:s'),
            ]);
        } catch (\Exception $e) {
            Logger::error('Failed to queue async event: ' . $e->getMessage(), ['event' => $event]);
        }
    }
}