<?php

namespace WillySoft;

abstract class Route
{
    static array  $groups = [
        []
    ];

    static string $prefix = '';

    static function match(string $methods, string $path, callable $callback)
    {
        if (!in_array($_SERVER['REQUEST_METHOD'],
            array_map(strtoupper(...), explode('|', $methods))
        )) return;

        static $request_path_parts_cache;
        if (!isset($request_path_parts_cache)) {
            $request_path_parts_cache = explode('/', urldecode(
                strtok($_SERVER['REQUEST_URI'], '?')
            ));
        }
        $request_path_parts = $request_path_parts_cache;
        $path_parts = explode('/', self::$prefix . $path);
        $callback_args = [];
        for ($i=0; $i < count($path_parts); $i++) {
            if (empty($path_parts[$i])) {
                continue;
            }
            if ($path_parts[$i][0] !== '$') {
                continue;
            }
            if (!isset($request_path_parts[$i])) {
                return;
            }
            if ($path_parts[$i][-1] !== '?' && empty($request_path_parts[$i])) {
                return;
            }
            if (!empty($request_path_parts[$i])) {
                array_push($callback_args, $request_path_parts[$i]);
            }
            $request_path_parts[$i] = $path_parts[$i];
        }
        if (
            implode('/', $path_parts) !== implode('/', $request_path_parts)
        ) return;

        foreach (self::$groups as $middlewares) {
            foreach ($middlewares as $middleware) {
                $middleware();
            }
        }
        $callback(...$callback_args);
        die();
    }

    static function get(string $path, callable $callback)     { self::match('get', $path, $callback); }
    static function post(string $path, callable $callback)    { self::match('post', $path, $callback); }
    static function put(string $path, callable $callback)     { self::match('put', $path, $callback); }
    static function patch(string $path, callable $callback)   { self::match('patch', $path, $callback); }
    static function delete(string $path, callable $callback)  { self::match('delete', $path, $callback); }
    static function options(string $path, callable $callback) { self::match('options', $path, $callback); }
    static function form(string $path, callable $callback)    { self::match('get|post', $path, $callback); }
    static function any(string $path, callable $callback)     { self::match('get|post|put|patch|delete|options', $path, $callback); }

    static function middleware(callable|array $middlewares)
    {
        if (!is_array($middlewares)) {
            $middlewares = [$middlewares];
        }
        foreach ($middlewares as $middleware) {
            array_push(
                self::$groups[array_key_last(self::$groups)], $middleware
            );
        }
    }

    static function group(string $prefix = '', ?callable $callback = null)
    {
        if (!str_starts_with($_SERVER['REQUEST_URI'], self::$prefix . $prefix)) {
            return;
        }
        self::$prefix = self::$prefix . $prefix;
        array_push(self::$groups, []);
        $callback();
        array_pop(self::$groups);
        self::$prefix = rtrim(self::$prefix, $prefix);
    }
}