<?php
namespace Misuzu\Http\Routing;

use InvalidArgumentException;
use Misuzu\Http\HttpRequestMessage;
use Misuzu\Http\HttpResponseMessage;

class Route {
    private $methods = [];
    private $path = '';
    private $children = [];
    private $filters = [];
    private $parentRoute = null;

    private $handlerClass = null;
    private $handlerMethod = null;

    public function __construct(array $methods, string $path, ?string $method = null, ?string $class = null) {
        $this->methods = array_map('strtoupper', $methods);
        $this->path = $path;
        $this->handlerClass = $class;
        $this->handlerMethod = $method;
    }

    public static function create(array $methods, string $path, ?string $method = null, ?string $class = null): self {
        return new Route($methods, $path, $method, $class);
    }
    public static function get(string $path, ?string $method = null, ?string $class = null): self {
        return self::create(['GET'], $path, $method, $class);
    }
    public static function post(string $path, ?string $method = null, ?string $class = null): self {
        return self::create(['POST'], $path, $method, $class);
    }
    public static function delete(string $path, ?string $method = null, ?string $class = null): self {
        return self::create(['DELETE'], $path, $method, $class);
    }
    public static function group(string $path, ?string $class = null): self {
        return self::create([''], $path, null, $class);
    }

    public function getHandlerClass(): string {
        return $this->handlerClass ?? ($this->parentRoute === null ? '' : $this->parentRoute->getHandlerClass());
    }
    public function setHandlerClass(string $class): self {
        $this->handlerClass = $class;
        return $this;
    }

    public function getHandlerMethod() {
        return $this->handlerMethod;
    }

    public function getParent(): ?self {
        return $this->parentRoute;
    }
    public function setParent(self $route): self {
        $this->parentRoute = $route;
        return $this;
    }

    public function getPath(): string {
        $path = $this->path;
        if($this->parentRoute !== null)
            $path = $this->parentRoute->getPath() . ($path[0] !== '.' ? '/' : '') . trim($path, '/');
        return $path;
    }
    public function setPath(string $path): self {
        $this->path = $path;
        return $this;
    }

    public function addFilters(string ...$filters): self {
        $this->filters = array_merge($this->filters, $filters);
        return $this;
    }
    public function getFilters(): array {
        $filters = $this->filters;
        if($this->parentRoute !== null)
            $filters += $this->parentRoute->getFilters();
        return $filters;
    }

    public function getChildren(): array {
        return $this->children;
    }
    public function addChildren(Route ...$routes): self {
        foreach($routes as $route)
            $this->children[] = $route->setParent($this);
        return $this;
    }

    public function match(HttpRequestMessage $request, array &$matches): bool {
        $matches = [];
        if(!in_array($request->getMethod(), $this->methods))
            return false;
        return preg_match('#^' . $this->getPath() . '$#', '/' . trim($request->getUri()->getPath(), '/'), $matches) === 1;
    }

    public function __serialize(): array {
        return [
            $this->methods,
            $this->getPath(),
            $this->getFilters(),
            $this->getHandlerClass(),
            $this->getHandlerMethod(),
        ];
    }

    public function __unserialize(array $data): void {
        [$this->methods, $this->path, $this->filters, $this->handlerClass, $this->handlerMethod] = $data;
    }
}