<?php

declare(strict_types=1);

namespace Apexl\Io\System\Services;

use Apexl\Io\Interfaces\AbstractHook;
use Apexl\Io\Interfaces\HookInterface;
use DI\Container;
use DI\DependencyException;
use DI\NotFoundException;
use InvalidArgumentException;

/**
 * Which hook keys a hook subscribes to, answered as cheaply as the hook allows.
 *
 * A hook implementing {@see HookInterface} answers from its class: boot reads the keys
 * without building anything, and the instance is only made if and when the hook fires. One
 * still on the deprecated {@see AbstractHook} has to be constructed to be asked, which
 * drags its whole dependency graph into every request — including the requests where it
 * never runs.
 *
 * Separate from ModuleManager because this is the decision worth testing on its own: that
 * the cheap path really is construction-free, and that the expensive one still works.
 */
final readonly class HookKeyResolver
{
    public function __construct(private Container $container) {}

    /**
     * @param class-string<HookInterface|AbstractHook>|HookInterface|AbstractHook $hook
     *
     * @return string[]
     *
     * @throws DependencyException
     * @throws NotFoundException
     */
    public function keys(string|object $hook): array
    {
        if (is_string($hook) && is_a($hook, HookInterface::class, true)) {
            return $hook::hooks();
        }

        $instance = is_string($hook) ? $this->container->get($hook) : $hook;

        if ($instance instanceof HookInterface) {
            return $instance::hooks();
        }

        if (!$instance instanceof AbstractHook) {
            throw new InvalidArgumentException(sprintf(
                '%s must implement %s.',
                is_object($hook) ? $hook::class : $hook,
                HookInterface::class,
            ));
        }

        return $instance->hooks();
    }
}
