<?php

namespace App\Services;

class HookManager
{
    protected array $actions = [];

    protected array $filters = [];

    public function registerAction(string $hook, callable $callback, int $priority = 10, ?string $appId = null): void
    {
        $this->actions[$hook][] = [
            'callback' => $callback,
            'priority' => $priority,
            'app_id' => $appId,
        ];

        $this->sortHooks($this->actions[$hook]);
    }

    public function registerFilter(string $hook, callable $callback, int $priority = 10, ?string $appId = null): void
    {
        $this->filters[$hook][] = [
            'callback' => $callback,
            'priority' => $priority,
            'app_id' => $appId,
        ];

        $this->sortHooks($this->filters[$hook]);
    }

    public function doAction(string $hook, mixed ...$args): void
    {
        if (! isset($this->actions[$hook])) {
            return;
        }

        foreach ($this->actions[$hook] as $entry) {
            call_user_func_array($entry['callback'], $args);
        }
    }

    public function applyFilter(string $hook, mixed $value, mixed ...$args): mixed
    {
        if (! isset($this->filters[$hook])) {
            return $value;
        }

        foreach ($this->filters[$hook] as $entry) {
            $value = call_user_func($entry['callback'], $value, ...$args);
        }

        return $value;
    }

    public function removeHooksByApp(string $appId): void
    {
        $this->actions = $this->removeAppHooks($this->actions, $appId);
        $this->filters = $this->removeAppHooks($this->filters, $appId);
    }

    public function getRegisteredHooks(?string $appId = null): array
    {
        if ($appId !== null) {
            return [
                'actions' => $this->filterByAppId($this->actions, $appId),
                'filters' => $this->filterByAppId($this->filters, $appId),
            ];
        }

        return [
            'actions' => $this->actions,
            'filters' => $this->filters,
        ];
    }

    protected function sortHooks(array &$hooks): void
    {
        usort($hooks, fn ($a, $b) => $a['priority'] <=> $b['priority']);
    }

    protected function removeAppHooks(array $hookGroups, string $appId): array
    {
        foreach ($hookGroups as $hook => &$entries) {
            $entries = array_values(array_filter(
                $entries,
                fn ($entry) => ($entry['app_id'] ?? null) !== $appId
            ));

            if (empty($entries)) {
                unset($hookGroups[$hook]);
            }
        }

        return $hookGroups;
    }

    protected function filterByAppId(array $hookGroups, string $appId): array
    {
        $result = [];

        foreach ($hookGroups as $hook => $entries) {
            $filtered = array_filter($entries, fn ($e) => ($e['app_id'] ?? null) === $appId);
            if (! empty($filtered)) {
                $result[$hook] = array_values($filtered);
            }
        }

        return $result;
    }
}
