<?php

namespace apexl\Io\includes;

use apexl\ClassEngine\EngineSingleton;
use apexl\Io\services\pathUtility;
use apexl\Io\utilityIncludes\route;

class Routes extends EngineSingleton
{
    protected static $Io;
    protected static $routes = [];
    protected static $baseRoute = '/';

    protected function __construct()
    {
        parent::__construct();
        self::$Io = System::$app;
    }

    /**
     * Function to set a base route for all routes. Applies at build time to allow contributed modules to override it.
     * @param string $route
     */
    public static function setBaseRoute($route = '/')
    {
        self::$baseRoute = $route;
    }

    /**
     * Get a route object
     * @param $name
     * @return false|mixed
     */
    public static function getRoute($name)
    {
        return self::$routes[$name] ?? null;
    }

    /**
     * Get all defined routes
     */
    public static function getRoutes()
    {
        return self::$routes;
    }

    /**
     * Get a route object
     * @param $name
     * @param array $replacements
     * @param bool $stripTags
     * @return false|mixed
     */
    public static function getRoutePattern($name, $replacements = [], $stripTags = true)
    {
        $route = self::$routes[$name] ?? false;
        if ($route) {
            $pattern = $route->getPattern();
            if (!empty($replacements)) {
                foreach ($replacements as $token => $value) {
                    $pattern = str_replace(['{'.$token.'}', '[', ']'], [$value, '', ''], (string) $pattern);
                }
            }
            //No Replacements? Remove any tokens and optionals.
            //@todo do with regex?
            if ($stripTags && str_contains((string) $pattern, '{')) {
                $parts = explode('/', (string) $pattern);
                $pattern = '';
                foreach ($parts as $part) {
                    if (!empty($part)) {
                        if (str_contains($part, '{')) {
                            $clean = ''; //dont include any tokens.
                        } else {
                            $clean = str_replace(['[', ']'], '', $part);
                        }
                        if (!empty($clean)) {
                            $pattern .= '/' . $clean;
                        }
                    }
                }
            }
            return $pattern;
        }
        return false;
    }

    public static function addRouteArg($name, $arg, $value)
    {
        //add arg to already built route, store reference in routes static
        $path = pathUtility::getInstance();
        $path->setCurrentRouteArgs($arg, $value);
        return isset(self::$routes[$name]) ? self::$routes[$name]->arg($arg, $value) : false;
    }

    /**
     * Set a roure object
     * @param $name
     * @param $route
     */
    public static function setRoute($name, $route)
    {
        self::$routes[$name] = $route;
    }

    /**
     * Add a new roue
     * @param $name
     * @param $pattern
     * @param $callable
     * @param string $verb
     * @param array $args
     */
    public static function addRoute($name, $pattern, $callable, $verb = 'GET', $args = [])
    {
        self::$routes[$name] = new route($name, $pattern, $callable, $verb, $args);
    }

    /**
     * Function to build the set routes into a format Slim can understand.
     */
    public static function buildRoutes()
    {
        self::$routes = Hook::processHook("routePreBuild", self::$routes);
        //add routes to the system
        foreach (self::$routes as $route) {
            //register the route
            $slimRoute = self::$Io->{$route->getVerb()}(rtrim((string) self::$baseRoute, '/').'/'.ltrim((string) $route->getPattern(), '/'), $route->getCallable());
            $slimRoute->setName($route->getName());
            if (!empty($route->getArgs())) {
                foreach ($route->getArgs() as $argName => $argVal) {
                    $slimRoute->setArgument($argName, $argVal);
                }
            }
        }
    }

    /**
     * @param $name
     * @param $callable
     */
    public static function get($name, string $pattern, $callable)
    {
        self::addRoute($name, $pattern, $callable, 'get');
    }

    /**
     * @param $name
     * @param $callable
     */
    public static function post($name, string $pattern, $callable)
    {
        self::addRoute($name, $pattern, $callable, 'post');
    }

    /**
     * @param $name
     * @param $callable
     */
    public static function put($name, string $pattern, $callable)
    {
        self::addRoute($name, $pattern, $callable, 'put');
    }

    /**
     * @param $name
     * @param $callable
     */
    public static function delete($name, string $pattern, $callable)
    {
        self::addRoute($name, $pattern, $callable, 'delete');
    }
}
