<?php

declare(strict_types=1);

namespace App\Core;

use App\Core\Router;
use App\Core\Request;
use App\Core\Response;
use App\Core\Session;
use App\Core\TenantManager;
use Exception;

/**
 * Classe Principal da Aplicação
 * Gerencia o ciclo de vida da aplicação
 */
class Application
{
    private Router $router;
    private Request $request;
    private Response $response;
    private Session $session;
    private ?TenantManager $tenantManager = null;
    private array $config;

    public function __construct()
    {
        $this->config = require \ROOT_PATH . '/config/app.php';
        $this->loadConfiguration();
        $this->initializeComponents();
    }

    /**
     * Carrega configurações globais
     */
    private function loadConfiguration(): void
    {
        // Define o timezone
        date_default_timezone_set($this->config['timezone']);

        // Verificar se é requisição AJAX/JSON - SEMPRE desabilitar display_errors
        $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
        $isJsonRequest = isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false;
        $path = $_SERVER['REQUEST_URI'] ?? '';
        $isJsonRoute = strpos($path, '/conciliacao/processar') !== false || strpos($path, '/buscar') !== false;

        // Se for requisição que retorna JSON, SEMPRE desabilitar display_errors
        if ($isAjax || $isJsonRequest || $isJsonRoute) {
            error_reporting(E_ALL);
            ini_set('display_errors', '0'); // NUNCA exibir erros em requisições JSON
            ini_set('log_errors', '1');
            ini_set('error_log', \ROOT_PATH . '/storage/logs/php-errors.log');
        } else {
            // Configurações de erro baseadas no ambiente
            if ($this->config['debug']) {
                error_reporting(E_ALL);
                ini_set('display_errors', '1');
            } else {
                error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
                ini_set('display_errors', '0');
                ini_set('log_errors', '1');
                ini_set('error_log', \ROOT_PATH . '/storage/logs/php-errors.log');
            }
        }

        // Configurações de segurança
        // NOTA: Configurações de sessão são feitas na classe Session
        // para permitir isolamento entre diferentes instalações
    }

    /**
     * Inicializa componentes da aplicação
     */
    private function initializeComponents(): void
    {
        $this->request = new Request();
        $this->response = new Response();
        $this->session = new Session($this->config['session']);
        $this->router = new Router($this->request, $this->response);

        // Inicializa o gerenciador de tenants
        $this->tenantManager = new TenantManager();
    }

    /**
     * Executa a aplicação
     */
    public function run(): void
    {
        try {
            // MODO SIMPLES: Sem multi-tenant
            // Se DB_TENANT_PREFIX não existe, pula identificação de tenant
            if (!empty($_ENV['DB_TENANT_PREFIX'])) {
                // Identifica o tenant baseado no domínio/subdomínio
                $tenant = $this->tenantManager->identifyTenant($this->request);

                if ($tenant) {
                    $this->tenantManager->setCurrentTenant($tenant);
                }
            }

            // Carrega as rotas
            $this->loadRoutes();

            // Despacha a requisição
            $this->router->dispatch();
        } catch (Exception $e) {
            $this->handleException($e);
        }
    }

    /**
     * Carrega as rotas da aplicação
     */
    private function loadRoutes(): void
    {
        $router = $this->router;
        require \ROOT_PATH . '/routes/web.php';
        require \ROOT_PATH . '/routes/api.php';
    }

    /**
     * Trata exceções da aplicação
     */
    private function handleException(Exception $e): void
    {
        // Registra o erro
        $this->logError($e);

        // Verificar se é requisição AJAX
        $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH'])
            && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';

        // Se for requisição AJAX, retornar JSON
        if ($isAjax) {
            $this->response->setStatusCode(500);
            $this->response->json([
                'success' => false,
                'message' => $this->config['debug']
                    ? $e->getMessage()
                    : 'Ocorreu um erro interno. Por favor, tente novamente.',
                'errors' => $this->config['debug'] ? [
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                    'trace' => $e->getTraceAsString()
                ] : []
            ], 500);
            return;
        }

        // Caso contrário, renderizar página de erro
        try {
            $errorController = new \App\Controllers\ErrorController($this->request, $this->response);
            $message = $this->config['debug']
                ? $e->getMessage() . "\n" . $e->getFile() . ":" . $e->getLine()
                : null;
            $errorController->serverError($message);
        } catch (\Exception $fallbackException) {
            // Fallback final se não conseguir renderizar a página de erro
            http_response_code(500);
            echo "<h1>Erro interno do servidor</h1>";
            if ($this->config['debug']) {
                echo "<p><strong>Mensagem:</strong> " . htmlspecialchars($e->getMessage()) . "</p>";
                echo "<p><strong>Arquivo:</strong> " . htmlspecialchars($e->getFile()) . ":" . $e->getLine() . "</p>";
                echo "<pre>" . htmlspecialchars($e->getTraceAsString()) . "</pre>";
            }
        }
    }

    /**
     * Registra erro no log
     */
    private function logError(Exception $e): void
    {
        $logFile = \ROOT_PATH . '/storage/logs/app-' . date('Y-m-d') . '.log';
        $message = sprintf(
            "[%s] %s: %s in %s:%d\nStack trace:\n%s\n\n",
            date('Y-m-d H:i:s'),
            get_class($e),
            $e->getMessage(),
            $e->getFile(),
            $e->getLine(),
            $e->getTraceAsString()
        );
        error_log($message, 3, $logFile);
    }

    /**
     * Retorna o router
     */
    public function getRouter(): Router
    {
        return $this->router;
    }

    /**
     * Retorna a configuração
     */
    public function getConfig(string $key = null)
    {
        if ($key === null) {
            return $this->config;
        }

        return $this->config[$key] ?? null;
    }
}
