<?php

declare(strict_types=1);

namespace App\Domain\Plugin;

class Hook
{
    /** @var array<string, list<callable>> */
    protected static array $listeners = [];

    public static function listen(string $event, callable $listener): void
    {
        static::$listeners[$event][] = $listener;
    }

    public static function dispatch(string $event, mixed ...$payload): void
    {
        foreach (static::$listeners[$event] ?? [] as $listener) {
            $listener(...$payload);
        }
    }

    /**
     * Collect string fragments from listeners (for theme injection points).
     */
    public static function gather(string $event, string $initial = '', mixed ...$payload): string
    {
        $buffer = $initial;

        foreach (static::$listeners[$event] ?? [] as $listener) {
            $result = $listener($buffer, ...$payload);
            if (is_string($result)) {
                $buffer = $result;
            }
        }

        return $buffer;
    }

    /**
     * Merge array fragments from listeners (for Filament schema/columns/actions).
     *
     * @param  array<int|string, mixed>  $initial
     * @return array<int|string, mixed>
     */
    public static function collect(string $event, array $initial = [], mixed ...$payload): array
    {
        $items = $initial;

        foreach (static::$listeners[$event] ?? [] as $listener) {
            $result = $listener($items, ...$payload);
            if (! is_array($result)) {
                continue;
            }

            foreach ($result as $key => $value) {
                if (is_int($key)) {
                    $items[] = $value;
                } else {
                    $items[$key] = $value;
                }
            }
        }

        return $items;
    }

    /**
     * Pipe a value through listeners (each may replace it).
     */
    public static function filter(string $event, mixed $value, mixed ...$payload): mixed
    {
        foreach (static::$listeners[$event] ?? [] as $listener) {
            $value = $listener($value, ...$payload);
        }

        return $value;
    }

    /**
     * Registered listeners for an event, for callers that need to fold results
     * themselves instead of letting each listener replace the value outright.
     *
     * @return list<callable>
     */
    public static function listeners(string $event): array
    {
        return static::$listeners[$event] ?? [];
    }

    public static function flush(): void
    {
        static::$listeners = [];
    }
}
