<?php

declare(strict_types=1);

class HookSystem
{
    private static array $actions = [];
    private static array $filters = [];
    private static array $actionsSorted = [];
    private static array $filtersSorted = [];

    public static function addAction(string $hook, callable $callback, int $priority = 10): void
    {
        self::$actions[$hook][$priority][] = $callback;
        unset(self::$actionsSorted[$hook]);
    }

    public static function doAction(string $hook, mixed ...$args): void
    {
        if (empty(self::$actions[$hook])) {
            return;
        }

        if (!isset(self::$actionsSorted[$hook])) {
            $hooks = self::$actions[$hook];
            ksort($hooks);
            self::$actionsSorted[$hook] = $hooks;
        }

        foreach (self::$actionsSorted[$hook] as $callbacks) {
            foreach ($callbacks as $callback) {
                try {
                    $callback(...$args);
                } catch (\Throwable $e) {
                    error_log("HookSystem action '{$hook}': " . $e->getMessage());
                }
            }
        }
    }

    public static function addFilter(string $hook, callable $callback, int $priority = 10): void
    {
        self::$filters[$hook][$priority][] = $callback;
        unset(self::$filtersSorted[$hook]);
    }

    public static function applyFilter(string $hook, mixed $value, mixed ...$args): mixed
    {
        if (empty(self::$filters[$hook])) {
            return $value;
        }

        if (!isset(self::$filtersSorted[$hook])) {
            $hooks = self::$filters[$hook];
            ksort($hooks);
            self::$filtersSorted[$hook] = $hooks;
        }

        foreach (self::$filtersSorted[$hook] as $callbacks) {
            foreach ($callbacks as $callback) {
                try {
                    $value = $callback($value, ...$args);
                } catch (\Throwable $e) {
                    error_log("HookSystem filter '{$hook}': " . $e->getMessage());
                }
            }
        }

        return $value;
    }

    public static function hasAction(string $hook): bool
    {
        return !empty(self::$actions[$hook]);
    }

    public static function hasFilter(string $hook): bool
    {
        return !empty(self::$filters[$hook]);
    }

    public static function removeAction(string $hook): void
    {
        unset(self::$actions[$hook], self::$actionsSorted[$hook]);
    }

    public static function removeFilter(string $hook): void
    {
        unset(self::$filters[$hook], self::$filtersSorted[$hook]);
    }
}
