<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Registry;

use PpPhp\Core\Platform\Event\Contracts\EventListenerInterface;

final class EventListenerRegistry
{
    private array $records = [];
    private array $eventIndex = [];
    private array $tagIndex = [];

    public function register(
        string $eventName,
        mixed $listener,
        int $priority = 0,
        array $tags = [],
        array $metadata = [],
        bool $enabled = true,
        bool $once = false,
        string $id = ''
    ): ListenerRecord {
        $record = new ListenerRecord($eventName, $listener, $priority, $id, $tags, $metadata, $enabled, $once);
        $this->records[$record->id()] = $record;
        $this->eventIndex[$eventName][$record->id()] = $record->id();

        foreach ($tags as $tag) {
            $this->tagIndex[(string) $tag][$record->id()] = $record->id();
        }

        return $record;
    }

    public function unregister(string $id): bool
    {
        if (!isset($this->records[$id])) {
            return false;
        }

        $record = $this->records[$id];
        unset($this->records[$id], $this->eventIndex[$record->eventName()][$id]);

        foreach ($this->tagIndex as $tag => $ids) {
            unset($this->tagIndex[$tag][$id]);
        }

        return true;
    }

    public function listenersFor(string $eventName): array
    {
        $records = [];

        foreach ($this->eventIndex[$eventName] ?? [] as $id) {
            if (isset($this->records[$id]) && $this->records[$id]->enabled()) {
                $records[] = $this->records[$id];
            }
        }

        usort($records, static fn (ListenerRecord $a, ListenerRecord $b): int => $b->priority() <=> $a->priority());

        return $records;
    }

    public function tagged(string $tag): array
    {
        $records = [];

        foreach ($this->tagIndex[$tag] ?? [] as $id) {
            if (isset($this->records[$id])) {
                $records[] = $this->records[$id];
            }
        }

        return $records;
    }

    public function has(string $id): bool { return isset($this->records[$id]); }
    public function get(string $id): ?ListenerRecord { return $this->records[$id] ?? null; }
    public function all(): array { return array_values($this->records); }
    public function events(): array { return array_keys($this->eventIndex); }
    public function count(): int { return count($this->records); }

    public function snapshot(): ListenerRegistrySnapshot
    {
        return new ListenerRegistrySnapshot($this->events(), $this->all(), $this->count());
    }

    public function toArray(): array
    {
        return $this->snapshot()->toArray();
    }
}
