<?php

declare(strict_types=1);

namespace Apexl\Io\Routing;

use Slim\Interfaces\RouteCollectorProxyInterface;
use Slim\Interfaces\RouteGroupInterface;
use Slim\Interfaces\RouteInterface;
use Slim\Routing\RouteCollector;

/**
 * Builds IoRoute instead of Slim's Route.
 *
 * `createRoute()` is Slim's extension point for this, and `AppFactory::createFromContainer()`
 * takes whatever is bound to `RouteCollectorInterface`, so binding this is the whole
 * installation.
 */
final class IoRouteCollector extends RouteCollector
{
    /**
     * @param string[] $methods
     * @param callable|array{class-string, string}|string $callable
     */
    protected function createRoute(array $methods, string $pattern, $callable): RouteInterface
    {
        // Everything registered anywhere arrives here, so this catches an ungrouped route
        // declared without a leading slash. A grouped one is checked by IoRouteCollectorProxy
        // instead: by this point its prefix has been concatenated, and `/admin` + `users`
        // reaches us as the entirely well-formed `/adminusers`.
        if ($pattern !== '' && !str_starts_with($pattern, '/')) {
            throw RoutePatternException::for($pattern);
        }

        return new IoRoute(
            $methods,
            $pattern,
            $callable,
            $this->responseFactory,
            $this->callableResolver,
            $this->container,
            $this->defaultInvocationStrategy,
            $this->routeGroups,
            $this->routeCounter,
        )->collectedBy($this);
    }

    /**
     * @param string|callable $callable
     */
    protected function createGroup(string $pattern, $callable): RouteGroupInterface
    {
        if ($pattern !== '' && !str_starts_with($pattern, '/')) {
            throw RoutePatternException::for($pattern);
        }

        return new IoRouteGroup($pattern, $callable, $this->callableResolver, $this->createProxy($pattern));
    }

    /** The closure a group is handed, which checks a route's own pattern before prefixing. */
    protected function createProxy(string $pattern): RouteCollectorProxyInterface
    {
        return new IoRouteCollectorProxy(
            $this->responseFactory,
            $this->callableResolver,
            $this->container,
            $this,
            $pattern,
        );
    }
}
