<?php

declare(strict_types=1);

namespace App\Framework\Router\ValueObjects;

use App\Framework\Http\Method;

final class RouteGroupBuilder
{
    /** @var array<GroupRoute> */
    private array $routes = [];

    /** @var array<string> */
    private array $middleware = [];

    public function __construct(
        private readonly RoutePath $prefix
    ) {
    }

    /**
     * Add middleware to this group
     *
     * @param string ...$middleware
     */
    public function middleware(string ...$middleware): self
    {
        $this->middleware = array_merge($this->middleware, $middleware);

        return $this;
    }

    /**
     * Add a GET route to the group
     */
    public function get(RoutePath $path, string $handler, ?string $name = null): self
    {
        return $this->addRoute(Method::GET, $path, $handler, $name);
    }

    /**
     * Add a POST route to the group
     */
    public function post(RoutePath $path, string $handler, ?string $name = null): self
    {
        return $this->addRoute(Method::POST, $path, $handler, $name);
    }

    /**
     * Add a PUT route to the group
     */
    public function put(RoutePath $path, string $handler, ?string $name = null): self
    {
        return $this->addRoute(Method::PUT, $path, $handler, $name);
    }

    /**
     * Add a DELETE route to the group
     */
    public function delete(RoutePath $path, string $handler, ?string $name = null): self
    {
        return $this->addRoute(Method::DELETE, $path, $handler, $name);
    }

    /**
     * Add a PATCH route to the group
     */
    public function patch(RoutePath $path, string $handler, ?string $name = null): self
    {
        return $this->addRoute(Method::PATCH, $path, $handler, $name);
    }

    /**
     * Add multiple HTTP methods for the same path
     *
     * @param array<Method> $methods
     */
    public function match(array $methods, RoutePath $path, string $handler, ?string $name = null): self
    {
        foreach ($methods as $method) {
            $this->addRoute($method, $path, $handler, $name);
        }

        return $this;
    }

    /**
     * Add a route that responds to all HTTP methods
     */
    public function any(RoutePath $path, string $handler, ?string $name = null): self
    {
        return $this->match([
            Method::GET,
            Method::POST,
            Method::PUT,
            Method::DELETE,
            Method::PATCH,
            Method::HEAD,
            Method::OPTIONS,
        ], $path, $handler, $name);
    }

    /**
     * Add a nested route group
     */
    public function group(RoutePath $nestedPrefix, callable $callback): self
    {
        $nestedBuilder = new self($nestedPrefix);
        $callback($nestedBuilder);

        $nestedGroup = $nestedBuilder->build();

        foreach ($nestedGroup->getCompiledRoutes() as $route) {
            $this->routes[] = $route;
        }

        return $this;
    }

    /**
     * Add a resource route collection (RESTful routes)
     */
    public function resource(string $name, string $controller): self
    {
        $resourcePath = RoutePath::fromString($name);
        $idPlaceholder = Placeholder::fromString('id');

        return $this
            ->get($resourcePath, $controller . '::index', $name . '.index')
            ->get($resourcePath->append('create'), $controller . '::create', $name . '.create')
            ->post($resourcePath, $controller . '::store', $name . '.store')
            ->get($resourcePath->append($idPlaceholder), $controller . '::show', $name . '.show')
            ->get($resourcePath->append($idPlaceholder)->append('edit'), $controller . '::edit', $name . '.edit')
            ->put($resourcePath->append($idPlaceholder), $controller . '::update', $name . '.update')
            ->patch($resourcePath->append($idPlaceholder), $controller . '::update', $name . '.update')
            ->delete($resourcePath->append($idPlaceholder), $controller . '::destroy', $name . '.destroy');
    }

    /**
     * Add an API resource route collection (no create/edit forms)
     */
    public function apiResource(string $name, string $controller): self
    {
        $resourcePath = RoutePath::fromString($name);
        $idPlaceholder = Placeholder::fromString('id');

        return $this
            ->get($resourcePath, $controller . '::index', $name . '.index')
            ->post($resourcePath, $controller . '::store', $name . '.store')
            ->get($resourcePath->append($idPlaceholder), $controller . '::show', $name . '.show')
            ->put($resourcePath->append($idPlaceholder), $controller . '::update', $name . '.update')
            ->patch($resourcePath->append($idPlaceholder), $controller . '::update', $name . '.update')
            ->delete($resourcePath->append($idPlaceholder), $controller . '::destroy', $name . '.destroy');
    }

    /**
     * Build the route group
     */
    public function build(): RouteGroup
    {
        return new RouteGroup($this->prefix, $this->middleware, ...$this->routes);
    }

    /**
     * Add a route with the specified method
     */
    private function addRoute(Method $method, RoutePath $path, string $handler, ?string $name): self
    {
        $route = GroupRoute::create(
            path: $path,
            method: $method,
            handler: $handler,
            name: $name,
            middleware: []
        );

        $this->routes[] = $route;

        return $this;
    }
}
