<?php

declare(strict_types=1);

namespace App\Core;

/**
 * Classe para gerenciar respostas HTTP
 */
class Response
{
    private int $statusCode = 200;
    private array $headers = [];
    private string $content = '';

    /**
     * Define o código de status HTTP
     */
    public function setStatusCode(int $code): self
    {
        $this->statusCode = $code;
        return $this;
    }

    /**
     * Adiciona um header
     */
    public function setHeader(string $name, string $value): self
    {
        $this->headers[$name] = $value;
        return $this;
    }

    /**
     * Define o conteúdo da resposta
     */
    public function setContent(string $content): self
    {
        $this->content = $content;
        return $this;
    }

    /**
     * Envia a resposta
     */
    public function send(): void
    {
        // Limpar qualquer output buffer antes de enviar
        while (ob_get_level() > 0) {
            @ob_end_clean();
        }

        // Garantir que não há nenhum output pendente
        if (ob_get_level() > 0) {
            @ob_end_flush();
            @ob_end_clean();
        }

        http_response_code($this->statusCode);

        foreach ($this->headers as $name => $value) {
            header("{$name}: {$value}");
        }

        echo $this->content;
        exit; // Garantir que nada mais seja executado após enviar a resposta
}

    /**
     * Responde com JSON
     */
    public function json(array $data, int $statusCode = 200): void
    {
        // Função helper para logar erros
        $logError = function($message, $context = []) {
            $logDir = ROOT_PATH . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs';
            if (!is_dir($logDir)) {
                @mkdir($logDir, 0755, true);
            }
            $logFile = $logDir . DIRECTORY_SEPARATOR . 'response-errors-' . date('Y-m-d') . '.log';
            $timestamp = date('Y-m-d H:i:s');
            $contextStr = !empty($context) ? ' | Context: ' . json_encode($context) : '';
            $logMessage = "[{$timestamp}] {$message}{$contextStr}\n";
            @file_put_contents($logFile, $logMessage, FILE_APPEND | LOCK_EX);
        };

        // Limpar qualquer output buffer antes de enviar JSON
        // IMPORTANTE: Limpar TODOS os níveis de output buffer
        $levelsCleared = 0;
        while (ob_get_level() > 0) {
            $output = @ob_get_clean();
            $levelsCleared++;
            if (!empty($output)) {
                $logError("Output buffer capturado antes de enviar JSON (nível {$levelsCleared})", [
                    'output_preview' => substr($output, 0, 500),
                    'output_length' => strlen($output),
                    'output_start' => substr($output, 0, 100)
                ]);
            }
        }

        // Garantir que não há nenhum output pendente
        if (ob_get_level() > 0) {
            @ob_end_flush();
            @ob_end_clean();
        }

        // Desabilitar exibição de erros para evitar output antes do JSON
        $oldDisplayErrors = ini_get('display_errors');
        ini_set('display_errors', '0');

        try {
            $this->setStatusCode($statusCode);
            $this->setHeader('Content-Type', 'application/json; charset=utf-8');
            $jsonContent = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

            if ($jsonContent === false) {
                $logError("Erro ao codificar JSON", [
                    'json_error' => json_last_error_msg(),
                    'data_preview' => substr(print_r($data, true), 0, 500)
                ]);
                $jsonContent = json_encode(['success' => false, 'message' => 'Erro ao processar resposta']);
            }

            $this->setContent($jsonContent);
            $this->send();
        } catch (Exception $e) {
            $logError("Exceção ao enviar JSON", [
                'message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            // Tentar enviar erro genérico
            http_response_code(500);
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'message' => 'Erro interno do servidor']);
        } finally {
            // Restaurar configuração
            ini_set('display_errors', $oldDisplayErrors);
        }
    }

    /**
     * Redireciona para uma URL
     * NOTA: Não adiciona base path aqui, pois o BaseController já processa
     */
    public function redirect(string $url, int $statusCode = 302): void
    {
        // Se a URL não começa com http/https, assume que já foi processada pelo BaseController
        // ou é uma URL relativa que deve ser usada como está
        if (!preg_match('/^https?:\/\//', $url)) {
            // Verifica se a URL já contém o base path detectado
            $basePath = \App\Helpers\UrlHelper::basePath();
            if ($basePath !== '' && strpos($url, $basePath) === 0) {
                // URL já contém o base path, usar como está
            } elseif ($basePath !== '' && $url[0] === '/') {
                // URL começa com / mas não tem base path, adicionar
                $url = \App\Helpers\UrlHelper::url($url);
            }
            // Se basePath está vazio ou URL não começa com /, usar como está
        }
        $this->setStatusCode($statusCode);
        header("Location: {$url}", true, $statusCode);
        exit;
    }

    /**
     * Retorna uma view
     */
    public function view(string $view, array $data = []): void
    {
        extract($data);

        $viewFile = \ROOT_PATH . "/views/{$view}.php";

        if (!file_exists($viewFile)) {
            throw new \Exception("View não encontrada: {$view}");
        }

        ob_start();
        require $viewFile;
        $content = ob_get_clean();

        $this->setContent($content);
        $this->send();
    }

    /**
     * Responde com erro 404
     */
    public function notFound(string $message = 'Página não encontrada'): void
    {
        $this->setStatusCode(404);

        // 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->json(['error' => $message, 'code' => 404], 404);
            return;
        }

        // Caso contrário, renderizar página de erro
        try {
            $request = new \App\Core\Request();
            $errorController = new \App\Controllers\ErrorController($request, $this);
            $errorController->notFound();
        } catch (\Exception $e) {
            // Fallback se não conseguir renderizar a página de erro
            http_response_code(404);
            echo "<h1>404 - Página não encontrada</h1><p>{$message}</p>";
        }
    }

    /**
     * Responde com erro 403
     */
    public function forbidden(string $message = 'Acesso negado'): void
    {
        $this->setStatusCode(403);

        // Verificar se é requisição AJAX (usando a mesma lógica do Request::isAjax())
        $isAjax = false;

        // Verificar header X-Requested-With
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])
            && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
            $isAjax = true;
        }

        // Verificar se é requisição fetch (Content-Type: application/json ou multipart/form-data)
        if (!$isAjax) {
            $contentType = $_SERVER['HTTP_CONTENT_TYPE'] ?? '';
            if (strpos($contentType, 'application/json') !== false
                || strpos($contentType, 'multipart/form-data') !== false) {
                // Se for POST/PUT/PATCH com Content-Type específico, provavelmente é AJAX
                $method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
                if (in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) {
                    $isAjax = true;
                }
            }
        }

        // Se for requisição AJAX, retornar JSON
        if ($isAjax) {
            $this->json(['success' => false, 'error' => $message, 'code' => 403], 403);
            return;
        }

        // Caso contrário, renderizar página de erro
        try {
            $request = new \App\Core\Request();
            $errorController = new \App\Controllers\ErrorController($request, $this);
            $errorController->forbidden();
        } catch (\Exception $e) {
            // Fallback se não conseguir renderizar a página de erro
            http_response_code(403);
            echo "<h1>403 - Acesso negado</h1><p>{$message}</p>";
        }
    }

    /**
     * Responde com erro 401
     */
    public function unauthorized(string $message = 'Não autenticado'): void
    {
        $this->setStatusCode(401);
        $this->json(['error' => $message, 'code' => 401], 401);
    }

    /**
     * Responde com sucesso
     */
    public function success(string $message, array $data = []): void
    {
        $this->json([
            'success' => true,
            'message' => $message,
            'data' => $data
        ]);
    }

    /**
     * Responde com erro
     */
    public function error(string $message, array $errors = [], int $statusCode = 400): void
    {
        // ÚLTIMA VERIFICAÇÃO: Se há boleto Shipay gerado, retornar sucesso
        $chargeIdGlobal = $GLOBALS['__shipay_charge_id'] ?? null;
        if (!empty($chargeIdGlobal)) {
            error_log("[Response::error] ✅ Boleto Shipay encontrado no Response::error! Retornando sucesso.");
            $this->json([
                'success' => true,
                'message' => 'Cobrança Shipay gerada com sucesso! (Houve um erro ao processar)',
                'boleto' => [
                    'charge_id' => $chargeIdGlobal,
                    'status' => $GLOBALS['__shipay_status'] ?? 'pending',
                    'linha_digitavel' => $GLOBALS['__shipay_linha_digitavel'] ?? null,
                    'codigo_barras' => $GLOBALS['__shipay_codigo_barras'] ?? null,
                    'qrcode' => $GLOBALS['__shipay_qr_code'] ?? null,
                    'qrcode_text' => $GLOBALS['__shipay_qr_code_text'] ?? null,
                    'pdf_url' => $GLOBALS['__shipay_pdf_url'] ?? null,
                ],
                'tipo' => $GLOBALS['__shipay_tipo'] ?? 'pix',
                'existente' => false,
                'aviso' => 'Boleto gerado, mas houve erro: ' . $message
            ], 200);
            return;
        }

        $this->json([
            'success' => false,
            'message' => $message,
            'errors' => $errors
        ], $statusCode);
    }
}

