<?php

namespace SoftminalB2B\Http\Routes;

use WP_Error;

if (!defined('ABSPATH')) {
    exit;
}

class Route
{
    private static $routes = [];
    private static $prefix = '';
    private static $middleware = [];
    private static $namespace = '';
    private static $lastRoute = null;

    private $pendingPrefix = '';
    private $pendingMiddleware = [];

    public static function prefix($prefix)
    {
        $instance = new static();
        $instance->pendingPrefix = '/' . trim($prefix, '/');
        return $instance;
    }

    public static function middleware($middleware)
    {
        $instance = new static();
        $instance->pendingMiddleware = is_array($middleware) ? $middleware : [$middleware];
        return $instance;
    }

    public function group(callable $callback)
    {
        $previousPrefix = self::$prefix;
        $previousMiddleware = self::$middleware;

        self::$prefix = $previousPrefix . $this->pendingPrefix;
        self::$middleware = array_merge($previousMiddleware, $this->pendingMiddleware);

        $callback();

        self::$prefix = $previousPrefix;
        self::$middleware = $previousMiddleware;

        return $this;
    }

    public static function get($endpoint, $controller)
    {
        return self::addRoute('GET', $endpoint, $controller);
    }

    public static function post($endpoint, $controller)
    {
        return self::addRoute('POST', $endpoint, $controller);
    }

    public static function put($endpoint, $controller)
    {
        return self::addRoute('PUT', $endpoint, $controller);
    }

    public static function patch($endpoint, $controller)
    {
        return self::addRoute('PATCH', $endpoint, $controller);
    }

    public static function delete($endpoint, $controller)
    {
        return self::addRoute('DELETE', $endpoint, $controller);
    }

    private static function addRoute($method, $endpoint, $controller, $params = [])
    {
        $endpoint = self::$prefix . '/' . trim($endpoint, '/');

        if (is_array($controller)) {
            $handler = $controller[0];
            $action = $controller[1];
        } else {
            $handler = $controller;
            $action = 'index';
        }

        if (self::$namespace) {
            $handler = self::$namespace . '\\' . $handler;
        }

        $parts = explode('/', $endpoint);
        $pattern = [];
        $parameters = [];

        foreach ($parts as $part) {
            if (strpos($part, '{') === 0 && strpos($part, '}') === strlen($part) - 1) {
                $paramName = trim($part, '{}');
                $parameterPattern = isset($params[$paramName]) ? $params[$paramName] : '(?P<' . $paramName . '>[^/]+)';
                $pattern[] = $parameterPattern;
                $parameters[] = $paramName;
            } else {
                $pattern[] = $part;
            }
        }

        $route = [
            'method' => $method,
            'endpoint' => $endpoint,
            'controller' => $handler,
            'action' => $action,
            'middleware' => self::$middleware,
            'parameters' => $parameters,
            'pattern' => implode('/', $pattern)
        ];

        self::$routes[] = $route;
        self::$lastRoute = &self::$routes[count(self::$routes) - 1];

        return new static();
    }

    public static function resource($controller)
    {
        self::get('', $controller);
        self::get('{id}', [$controller, 'show']);
        self::post('', [$controller, 'store']);
        self::put('{id}', [$controller, 'update']);
        self::delete('{id}', [$controller, 'destroy']);

        return new static();
    }

    public static function registerRoutes($namespace = null) {
        if ($namespace === null) {
            $namespace = defined('SMB2B_API_NAMESPACE')
                ? SMB2B_API_NAMESPACE
                : 'smb2b/v1';
        }
        if (empty(self::$routes)) {
            return;
        }

        // Group by path first. register_rest_route() merges repeat calls for
        // the same path, so registering one method at a time silently drops
        // every method after the first — POST/PUT/DELETE would never exist.
        $byPattern = [];

        foreach (self::$routes as $route) {
            $pattern = !empty($route['parameters']) && isset($route['pattern'])
                ? $route['pattern']
                : $route['endpoint'];

            $byPattern[$pattern][] = $route;
        }

        foreach ($byPattern as $pattern => $routes) {
            $endpoints = [];

            foreach ($routes as $route) {
                $endpoints[] = [
                    'methods'  => $route['method'],
                    'callback' => function ($request) use ($route) {
                        try {
                            $controller = new $route['controller']();
                            $action = $route['action'];
                            $params = $request->get_params();

                            return $controller->$action($request, $params);
                        } catch (\Exception $e) {
                            return new \WP_Error(
                                'api_error',
                                $e->getMessage(),
                                ['status' => 500]
                            );
                        }
                    },
                    'permission_callback' => function ($request) use ($route) {
                        return self::handleMiddleware($request, $route);
                    },
                ];
            }

            register_rest_route($namespace, $pattern, $endpoints);
        }
    }

    private static function handleMiddleware($request, $route)
    {
        $middlewares = $route['middleware'] ?? [];
        $isPublic = in_array('public', $middlewares, true);

        // Extract capability from 'can:capability' middleware (default: edit_posts)
        $capability = 'edit_posts';
        foreach ($middlewares as $mw) {
            if (is_string($mw) && strpos($mw, 'can:') === 0) {
                $capability = substr($mw, 4);
                break;
            }
        }

        // Auth check (skipped for public routes)
        if (!$isPublic) {
            if (!is_user_logged_in()) {
                return new WP_Error(
                    'rest_unauthorized',
                    'You must be logged in.',
                    ['status' => 401]
                );
            }

            if (!current_user_can($capability)) {
                return new WP_Error(
                    'rest_forbidden',
                    'Insufficient permissions.',
                    ['status' => 403]
                );
            }
        }

        // Run custom middleware classes (skip built-in flags)
        foreach ($middlewares as $middleware) {
            if (!is_string($middleware) || $middleware === 'public' || strpos($middleware, 'can:') === 0) {
                continue;
            }

            try {
                if (!class_exists($middleware)) {
                    continue;
                }

                $instance = new $middleware();
                if (method_exists($instance, 'handle')) {
                    $result = $instance->handle($request);
                    if ($result instanceof WP_Error) {
                        return $result;
                    }
                    if ($result !== true) {
                        return new WP_Error(
                            'middleware_failed',
                            'Access denied',
                            ['status' => 403]
                        );
                    }
                }
            } catch (\Exception $e) {
                return new WP_Error(
                    'server_error',
                    'Internal server error',
                    ['status' => 500]
                );
            }
        }

        return true;
    }

    public function withPolicy($policy)
    {
        if (self::$lastRoute) {
            self::$lastRoute['middleware'][] = $policy;
        }
        return $this;
    }

    public static function getRegisteredRoutes() {
        return self::$routes;
    }
}
