<?php

declare(strict_types=1);

namespace App\Core;

/**
 * Sistema de Roteamento
 */
class Router
{
    private Request $request;
    private Response $response;
    private array $routes = [];
    private array $middleware = [];

    public function __construct(Request $request, Response $response)
    {
        $this->request = $request;
        $this->response = $response;
    }

    /**
     * Adiciona uma rota GET
     */
    public function get(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('GET', $path, $handler, $middleware);
    }

    /**
     * Adiciona uma rota POST
     */
    public function post(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('POST', $path, $handler, $middleware);
    }

    /**
     * Adiciona uma rota PUT
     */
    public function put(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('PUT', $path, $handler, $middleware);
    }

    /**
     * Adiciona uma rota DELETE
     */
    public function delete(string $path, $handler, array $middleware = []): void
    {
        $this->addRoute('DELETE', $path, $handler, $middleware);
    }

    /**
     * Adiciona uma rota para qualquer método
     */
    public function any(string $path, $handler, array $middleware = []): void
    {
        $methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
        foreach ($methods as $method) {
            $this->addRoute($method, $path, $handler, $middleware);
        }
    }

    /**
     * Adiciona um grupo de rotas com prefixo
     */
    public function group(string $prefix, callable $callback, array $middleware = []): void
    {
        $originalMiddleware = $this->middleware;
        $this->middleware = array_merge($this->middleware, $middleware);

        $callback($this, $prefix);

        $this->middleware = $originalMiddleware;
    }

    /**
     * Adiciona uma rota ao sistema
     */
    private function addRoute(string $method, string $path, $handler, array $middleware = []): void
    {
        // Normalizar o path
        $path = '/' . ltrim(rtrim($path, '/'), '/');
        if ($path === '//') {
            $path = '/';
        }

        $this->routes[$method][$path] = [
            'handler' => $handler,
            'middleware' => array_merge($this->middleware, $middleware),
        ];
    }

    /**
     * Despacha a requisição para a rota correspondente
     */
    public function dispatch(): void
    {
        $logFile = \ROOT_PATH . '/storage/logs/php-errors.log';
        $logDir = dirname($logFile);

        // Criar diretório se não existir
        if (!is_dir($logDir)) {
            mkdir($logDir, 0775, true);
        }

        $writeLog = function($message) use ($logFile) {
            $timestamp = date('Y-m-d H:i:s');
            $logMessage = "[{$timestamp}] [Router::dispatch] {$message}" . PHP_EOL;
            file_put_contents($logFile, $logMessage, FILE_APPEND);
        };

        $method = $this->request->method();
        $path = $this->request->path();

        $writeLog("=== INÍCIO dispatch ===");
        $writeLog("REQUEST_METHOD: {$method}");
        $writeLog("REQUEST_URI: " . ($_SERVER['REQUEST_URI'] ?? 'N/A'));
        $writeLog("Path processado: {$path}");
        $writeLog("Total de rotas registradas para {$method}: " . count($this->routes[$method] ?? []));

        // Normalizar o path (garantir que comece com / e não termine com /)
        $path = '/' . ltrim(rtrim($path, '/'), '/');
        if ($path === '//') {
            $path = '/';
        }

        $writeLog("Path normalizado: {$path}");

        // Procura por rota exata
        if (isset($this->routes[$method][$path])) {
            $writeLog("✅ Rota exata encontrada: {$path}");
            $route = $this->routes[$method][$path];
            $this->executeRoute($route, []);
            return;
        }

        $writeLog("❌ Rota exata não encontrada. Buscando rotas com parâmetros...");

        // Listar rotas disponíveis para debug
        if (!empty($this->routes[$method])) {
            $writeLog("Rotas disponíveis para {$method}:");
            foreach (array_keys($this->routes[$method]) as $routePath) {
                $writeLog("  - {$routePath}");
            }
        }

        // Procura por rota com parâmetros
        foreach ($this->routes[$method] ?? [] as $routePath => $route) {
            $pattern = $this->convertToRegex($routePath);
            $writeLog("Testando rota: {$routePath} (pattern: {$pattern})");

            if (preg_match($pattern, $path, $matches)) {
                $writeLog("✅ Rota com parâmetros encontrada: {$routePath}");
                array_shift($matches); // Remove o match completo
                $this->executeRoute($route, $matches);
                return;
            }
        }

        // Rota não encontrada
        $writeLog("❌❌❌ ROTA NÃO ENCONTRADA para {$method} {$path}");
        $writeLog("=== FIM dispatch (404) ===");
        $this->response->notFound('Rota não encontrada');
    }

    /**
     * Converte uma rota para regex
     */
    private function convertToRegex(string $route): string
    {
        // Substitui {param} por regex nomeado
        $pattern = preg_replace('/\{([a-zA-Z0-9_]+)\}/', '([^/]+)', $route);
        return '#^' . $pattern . '$#';
    }

    /**
     * Executa uma rota
     */
    private function executeRoute(array $route, array $params): void
    {
        $logFile = \ROOT_PATH . '/storage/logs/php-errors.log';
        $writeLog = function($message) use ($logFile) {
            $timestamp = date('Y-m-d H:i:s');
            $logMessage = "[{$timestamp}] [Router::executeRoute] {$message}" . PHP_EOL;
            file_put_contents($logFile, $logMessage, FILE_APPEND);
        };

        $writeLog("Executando rota. Handler: " . json_encode($route['handler']));

        // Executa middleware
        foreach ($route['middleware'] as $middleware) {
            if (is_string($middleware)) {
                $writeLog("Executando middleware: {$middleware}");
                $middlewareClass = "App\\Middleware\\{$middleware}";
                if (class_exists($middlewareClass)) {
                    $middlewareInstance = new $middlewareClass();
                    $result = $middlewareInstance->handle($this->request, $this->response);

                    if ($result === false) {
                        $writeLog("Middleware {$middleware} interrompeu a execução");
                        return; // Middleware interrompeu a execução
                    }
                    $writeLog("Middleware {$middleware} passou");
                } else {
                    $writeLog("Middleware {$middlewareClass} não encontrado");
                }
            }
        }

        $handler = $route['handler'];
        $writeLog("Chamando handler: " . json_encode($handler));

        // Limpar qualquer output buffer antes de executar o handler
        // Isso garante que erros/warnings não apareçam antes do JSON
        while (ob_get_level() > 0) {
            $output = ob_get_clean();
            if (!empty($output) && (strpos($path, '/conciliacao/processar') !== false || strpos($path, '/buscar') !== false)) {
                // Logar output inesperado para rotas JSON
                $logFile = \ROOT_PATH . '/storage/logs/router-output-' . date('Y-m-d') . '.log';
                $logMessage = "[" . date('Y-m-d H:i:s') . "] Output capturado antes do handler: " . substr($output, 0, 500) . "\n";
                @file_put_contents($logFile, $logMessage, FILE_APPEND | LOCK_EX);
            }
        }

        // Se for um array [Controller, method]
        if (is_array($handler)) {
            [$controller, $method] = $handler;

            if (is_string($controller)) {
                $controller = new $controller($this->request, $this->response);
            }

            call_user_func_array([$controller, $method], $params);
            return;
        }

        // Se for uma closure
        if (is_callable($handler)) {
            call_user_func_array($handler, array_merge([$this->request, $this->response], $params));
            return;
        }

        // Se for uma string no formato "Controller@method"
        if (is_string($handler) && strpos($handler, '@') !== false) {
            [$controllerName, $method] = explode('@', $handler);
            $controllerClass = "App\\Controllers\\{$controllerName}";

            // Tentar carregar o arquivo do controller se a classe não existir
            if (!class_exists($controllerClass)) {
                $controllerFile = \ROOT_PATH . "/src/Controllers/{$controllerName}.php";
                if (file_exists($controllerFile)) {
                    require_once $controllerFile;
                }
            }

            // Verificar se a classe existe após tentar carregar
            if (!class_exists($controllerClass)) {
                throw new \Exception("Controller não encontrado: {$controllerName}");
            }

            // Verificar se o método existe
            if (!method_exists($controllerClass, $method)) {
                throw new \Exception("Método não encontrado: {$controllerClass}::{$method}");
            }

            try {
                $writeLog("Criando instância do controller: {$controllerClass}");
                $controller = new $controllerClass($this->request, $this->response);
                $writeLog("Chamando método: {$method}");
                call_user_func_array([$controller, $method], $params);
                $writeLog("Método {$method} executado com sucesso");
                return;
            } catch (\Exception $e) {
                $writeLog("ERRO ao executar controller: " . $e->getMessage());
                $writeLog("Stack trace: " . $e->getTraceAsString());
                throw $e;
            }
        }

        throw new \Exception("Handler inválido para a rota");
    }

    /**
     * Gera URL para uma rota nomeada
     */
    public function url(string $name, array $params = []): string
    {
        // Implementação futura de rotas nomeadas
        return '';
    }
}
