<?php


namespace CubiStore\Web\Utils;

class Route
{
    const TYPE_ROOT = 'root';
    const TYPE_GROUP = 'group';
    const TYPE_CALL = 'call';

    /**
     * @var string
     */
    private $type;

    /**
     * @var string
     */
    private $path;

    /**
     * @var string
     */
    private $fullPath;

    /**
     * @var Route[]|null
     */
    private $children;

    /**
     * @var string|null
     */
    private $method;

    /**
     * @var string|null
     */
    private $call;

    private static function new(Route $parent, string $path, string $type): Route
    {
        if (strlen($path) > 0 && $path[0] !== '/') {
            throw new \RuntimeException("path should start with /");
        }

        if ($parent->type !== self::TYPE_GROUP && $parent->type !== self::TYPE_ROOT) {
            throw new \RuntimeException("Can only add child routes to a group or root route");
        }


        $route = new Route();
        $parent->add($route);
        $route->path = $path;
        $route->fullPath = $parent->fullPath . $path;
        $route->type = $type;

        return $route;
    }

    public static function group(Route $parent, string $path): Route
    {
        $route = static::new($parent, $path, self::TYPE_GROUP);
        $route->children = [];
        return $route;
    }

    /**
     * @param Route $parent
     * @param string $method
     * @param string $path
     * @param string $call
     * @return Route
     */
    public static function call(Route $parent, string $method, string $path, string $call): Route
    {
        $route = static::new($parent, $path, self::TYPE_CALL);
        $route->method = $method;
        $route->call = $call;
        return $route;
    }

    public static function root(): Route
    {
        $route = new Route();
        $route->type = self::TYPE_ROOT;
        $route->fullPath = $route->path = '/';
        $route->children = [];
        return $route;
    }

    public function add(Route $child): void
    {
        if ($this->type !== self::TYPE_GROUP && $this->type !== self::TYPE_ROOT) {
            throw new \RuntimeException("Only a group route can have child routes");
        }

        $this->children[] = $child;
    }

    private function json($value)
    {
        return json_encode($value, JSON_UNESCAPED_SLASHES);
    }

    public function compileRoute($var, $indent = 4): string
    {
        if ($this->type === self::TYPE_CALL) {
            return str_repeat(' ', $indent) . "${var}->map([" . $this->json($this->method) . "], " . $this->json($this->path) . ", " . $this->json($this->call) . ");";
        }

        if ($this->type === self::TYPE_GROUP) {
            $name = "\$group" . strlen($this->fullPath);
            $code = str_repeat(' ', $indent) . "${var}->group(" . $this->json($this->path) . ", function (RouteCollectorProxy $name) {\n";

            if ($this->children === null) {
                $this->children = [];
            }

            foreach ($this->children as $child) {
                $code .= $child->compileRoute($name, $indent + 4) . "\n";
            }
            return $code . str_repeat(' ', $indent) . "});";
        }

        if ($this->type === self::TYPE_ROOT) {
            $code = '';

            if ($this->children === null) {
                $this->children = [];
            }

            foreach ($this->children as $child) {
                $code .= $child->compileRoute($var, $indent) . "\n";
            }
            return $code;
        }

        throw new \RuntimeException("invalid type for route");
    }
}
