<?php
namespace App\Core;

/** Router sederhana dengan parameter {nama} dan middleware. */
class Router
{
    private array $routes = [];
    private array $middlewareGlobal = [];

    public function get(string $path, $handler, array $opts = []): self    { return $this->add('GET', $path, $handler, $opts); }
    public function post(string $path, $handler, array $opts = []): self   { return $this->add('POST', $path, $handler, $opts); }
    public function put(string $path, $handler, array $opts = []): self    { return $this->add('PUT', $path, $handler, $opts); }
    public function delete(string $path, $handler, array $opts = []): self { return $this->add('DELETE', $path, $handler, $opts); }
    public function any(string $path, $handler, array $opts = []): self
    {
        foreach (['GET','POST','PUT','DELETE'] as $m) $this->add($m, $path, $handler, $opts);
        return $this;
    }

    private function add(string $method, string $path, $handler, array $opts): self
    {
        $path = '/' . trim($path, '/');
        if ($path !== '/') $path = rtrim($path, '/');
        $regex = '#^' . preg_replace('#\{([a-zA-Z_][a-zA-Z0-9_]*)\}#', '(?P<$1>[^/]+)', $path) . '$#';
        $this->routes[$method][] = ['path' => $path, 'regex' => $regex, 'handler' => $handler, 'opts' => $opts];
        return $this;
    }

    public function globalMiddleware(callable $fn): void { $this->middlewareGlobal[] = $fn; }

    public function dispatch(): void
    {
        $method = Request::method();
        $uri    = Request::uri();

        $route = null; $params = [];
        foreach ($this->routes[$method] ?? [] as $r) {
            if (preg_match($r['regex'], $uri, $m)) {
                $route = $r;
                foreach ($m as $k => $v) if (!is_int($k)) $params[$k] = $v;
                break;
            }
        }

        if (!$route) {
            // Bila path cocok pada metode lain -> 405
            foreach ($this->routes as $mth => $rs) {
                if ($mth === $method) continue;
                foreach ($rs as $r) {
                    if (preg_match($r['regex'], $uri)) Response::abort(405, 'Metode ' . $method . ' tidak diizinkan pada alamat ini.');
                }
            }
            Response::abort(404, 'Alamat "' . htmlspecialchars($uri) . '" tidak tersedia di sistem.');
        }

        foreach ($this->middlewareGlobal as $mw) $mw($route, $params);

        // Pemeriksaan izin per rute
        if (!empty($route['opts']['perm'])) {
            [$mod, $act] = array_pad(explode('.', (string)$route['opts']['perm'], 2), 2, 'view');
            Acl::require($mod, $act);
        }
        if (!empty($route['opts']['csrf']) || in_array($method, ['POST','PUT','DELETE'], true)) {
            if (($route['opts']['csrf'] ?? true) !== false) Csrf::verifyOrFail();
        }

        $handler = $route['handler'];
        if (is_callable($handler)) { $handler($params); return; }

        // "NamaController@aksi"
        [$class, $action] = array_pad(explode('@', (string)$handler, 2), 2, 'index');
        $fqcn = 'App\\Controllers\\' . $class;
        if (!class_exists($fqcn)) Response::abort(500, 'Controller tidak ditemukan: ' . $class);
        $obj = new $fqcn();
        if (!method_exists($obj, $action)) Response::abort(500, "Aksi \"$action\" tidak tersedia pada $class.");
        $obj->$action($params);
    }

    public function routeCount(): int
    {
        $n = 0;
        foreach ($this->routes as $rs) $n += count($rs);
        return $n;
    }
}
