<?php
/**
 * ZLO Platform - Router
 * RESTful API routing system
 */

declare(strict_types=1);

class Router
{
    private array $routes = [];
    private array $middleware = [];
    private string $basePath = '';
    
    /**
     * Add a route
     */
    public function add(string $method, string $path, $handler, array $middleware = []): self
    {
        $this->routes[] = [
            'method' => strtoupper($method),
            'path' => $this->basePath . $path,
            'handler' => $handler,
            'middleware' => $middleware
        ];
        return $this;
    }
    
    /**
     * GET route
     */
    public function get(string $path, $handler, array $middleware = []): self
    {
        return $this->add('GET', $path, $handler, $middleware);
    }
    
    /**
     * POST route
     */
    public function post(string $path, $handler, array $middleware = []): self
    {
        return $this->add('POST', $path, $handler, $middleware);
    }
    
    /**
     * PUT route
     */
    public function put(string $path, $handler, array $middleware = []): self
    {
        return $this->add('PUT', $path, $handler, $middleware);
    }
    
    /**
     * PATCH route
     */
    public function patch(string $path, $handler, array $middleware = []): self
    {
        return $this->add('PATCH', $path, $handler, $middleware);
    }
    
    /**
     * DELETE route
     */
    public function delete(string $path, $handler, array $middleware = []): self
    {
        return $this->add('DELETE', $path, $handler, $middleware);
    }
    
    /**
     * Group routes with prefix
     */
    public function group(string $prefix, callable $callback, array $middleware = []): void
    {
        $previousBasePath = $this->basePath;
        $this->basePath .= $prefix;
        
        $previousMiddleware = $this->middleware;
        $this->middleware = array_merge($this->middleware, $middleware);
        
        $callback($this);
        
        $this->basePath = $previousBasePath;
        $this->middleware = $previousMiddleware;
    }
    
    /**
     * Dispatch request
     */
    public function dispatch(): void
    {
        $method = $_SERVER['REQUEST_METHOD'];
        $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        
        // Remove base directory from URI
        $scriptName = dirname($_SERVER['SCRIPT_NAME']);
        if (strpos($uri, $scriptName) === 0) {
            $uri = substr($uri, strlen($scriptName));
        }
        
        $uri = trim($uri, '/');
        
        // Handle preflight requests
        if ($method === 'OPTIONS') {
            $this->sendCorsHeaders();
            http_response_code(204);
            exit;
        }
        
        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) {
                continue;
            }
            
            $pattern = $this->convertToRegex($route['path']);
            
            if (preg_match($pattern, $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                
                // Execute middleware
                $allMiddleware = array_merge($this->middleware, $route['middleware']);
                foreach ($allMiddleware as $middleware) {
                    if (is_callable($middleware)) {
                        $result = $middleware();
                        if ($result === false) {
                            return;
                        }
                    }
                }
                
                // Execute handler
                $this->executeHandler($route['handler'], $params);
                return;
            }
        }
        
        // No route found
        $this->sendError('Route not found', 404);
    }
    
    /**
     * Convert route path to regex
     */
    private function convertToRegex(string $path): string
    {
        $pattern = preg_replace('/\{([^}]+)\}/', '(?P<$1>[^/]+)', $path);
        return '#^' . $pattern . '$#';
    }
    
    /**
     * Execute route handler
     */
    private function executeHandler($handler, array $params): void
    {
        if (is_callable($handler)) {
            $response = call_user_func($handler, $params);
            $this->sendResponse($response);
        } elseif (is_array($handler) && count($handler) === 2) {
            [$controller, $method] = $handler;
            
            if (!class_exists($controller)) {
                $this->sendError("Controller not found: {$controller}", 500);
                return;
            }
            
            $instance = new $controller();
            
            if (!method_exists($instance, $method)) {
                $this->sendError("Method not found: {$method}", 500);
                return;
            }
            
            $response = $instance->$method($params);
            $this->sendResponse($response);
        } else {
            $this->sendError('Invalid handler', 500);
        }
    }
    
    /**
     * Send JSON response
     */
    private function sendResponse($data): void
    {
        $this->sendCorsHeaders();
        header('Content-Type: application/json; charset=utf-8');
        
        if ($data === null) {
            http_response_code(204);
            exit;
        }
        
        $response = [
            'success' => true,
            'data' => $data
        ];
        
        echo json_encode($response, JSON_PRETTY_PRINT);
        exit;
    }
    
    /**
     * Send error response
     */
    public function sendError(string $message, int $code = 400, array $errors = []): void
    {
        $this->sendCorsHeaders();
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        
        $response = [
            'success' => false,
            'error' => [
                'code' => $code,
                'message' => $message
            ]
        ];
        
        if (!empty($errors)) {
            $response['error']['details'] = $errors;
        }
        
        echo json_encode($response, JSON_PRETTY_PRINT);
        exit;
    }
    
    /**
     * Send CORS headers
     */
    private function sendCorsHeaders(): void
    {
        $config = require __DIR__ . '/../config/config.php';
        $allowedOrigins = $config['security']['allowed_origins'] ?? ['*'];
        
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
        
        if (in_array('*', $allowedOrigins) || in_array($origin, $allowedOrigins)) {
            header("Access-Control-Allow-Origin: {$origin}");
        }
        
        header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-CSRF-Token');
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Max-Age: 86400');
    }
    
    /**
     * Get request body
     */
    public static function getBody(): array
    {
        $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
        
        if (strpos($contentType, 'application/json') !== false) {
            $json = file_get_contents('php://input');
            return json_decode($json, true) ?? [];
        }
        
        return $_POST;
    }
    
    /**
     * Get query parameters
     */
    public static function getQuery(): array
    {
        return $_GET;
    }
    
    /**
     * Get authorization token
     */
    public static function getAuthToken(): ?string
    {
        $headers = getallheaders();
        $auth = $headers['Authorization'] ?? '';
        
        if (preg_match('/Bearer\s+(.+)/', $auth, $matches)) {
            return $matches[1];
        }
        
        return null;
    }
}
