<?php

namespace classes;

class router
{
    private string $url;
    private string $method;
    private array $routes;
    private array $routeClasses; // Contains the classes that have the route_t trait

    public function __construct()
    {
        $this->url = $_SERVER['REQUEST_URI'] ?? ($argv[1] ?? '/'); // Get the URL (Or the first argument if it's a CLI request)
        $this->method = $_SERVER['REQUEST_METHOD'] ?? ($argv[2] ?? 'GET'); // Get the method (Or the second argument if it's a CLI request)
        $this->routes = [];
        $this->routeClasses = [];
    }

    public function add($route, $method, $function, array $permissions = []): void
    {
        $this->routes[] = ['route' => $route, 'method' => $method, 'function' => $function, 'permissions' => $permissions];
    }

    public function auto_load_routes(string $path): void
    {
        global $response;
        $files = scandir($path);
        foreach ( $files as $file ) {
            if ($file == '.' || $file == '..') {
                continue;
            }
            require_once $path . '/' . $file;
        }

        // Get all the classes in the files with the route_t trait
        $classes = get_declared_classes();
        foreach ( $classes as $class ) {
            if (in_array('traits\route_t', class_uses($class))) {
                $this->routeClasses[] = $class;
            }
        }

        // Try to run the routes, if there is an error, catch it and send an internal server error response
        try {
            $this->run();
        } catch (\Exception $e) {
            $response->internal_server_error($e->getMessage());
        }
    }

    public function run(): void
    {
        foreach ( $this->routeClasses as $class ) {
            $route = new $class();
            // Add the routes to the router
            $route->run();
        }

        $this->routeRequest();
    }

    private function routeRequest(): void
    {
        global $response;
        $matching_route_found = false;
        foreach ( $this->routes as $route ) {
            if ($this->doesRouteMatchCurrent($route['route']) && $route['method'] == $this->method) {
                $route['function']();
                $matching_route_found = true;
            }
        }

        if ($matching_route_found) {
            $response->matching_route_found();
        } else {
            $response->not_found();
        }
    }

    private function doesRouteMatchCurrent($route): bool
    {
        // Check if the route matches the current URL or if it matches the regex pattern
        // Remove the query string
        $this->url = explode('?', $this->url)[0];
        // Exact match
        if ($route == $this->url) {
            return true;
        }
        // Regex
        $route = str_replace('/', '\/', $route);
        $route = preg_replace('/{[a-zA-Z0-9]+}/', '([a-zA-Z0-9]+)', $route);
        if (preg_match('/^' . $route . '$/', $this->url)) {
            return true;
        }
        // None of the matches were found
        return false;
    }

    public function ERROR_HANDLER($callback): void
    {
        try {
            $callback();
        } catch (\Exception $e) {
            global $response;
            $response->internal_server_error($e->getMessage());
        }
    }

    public function countRoutes(): int
    {
        return count($this->routes);
    }

    public function getPermissions(): array
    {
        $permissions = [];
        foreach ( $this->routes as $route ) {
            $permissions = array_merge($permissions, $route['permissions']);
        }
        return $permissions;
    }

    public function getRoutes(): array
    {
        return $this->routes;
    }
}