<?php
// Download robusto do banco SQLite.
// O buffer inicia antes do bootstrap para impedir que avisos/saídas acidentais
// corrompam os headers ou o arquivo enviado ao navegador.
ob_start();
require __DIR__ . '/app/bootstrap.php';
require_roles('super_admin');

@set_time_limit(0);
@ignore_user_abort(true);
@ini_set('zlib.output_compression', '0');

$dbPath = (string)config('db_path');
if (!is_file($dbPath) || !is_readable($dbPath)) {
    while (ob_get_level() > 0) @ob_end_clean();
    http_response_code(404);
    exit('Banco não encontrado ou sem permissão de leitura.');
}

$tmpPath = dirname($dbPath) . '/.download-backup-' . bin2hex(random_bytes(8)) . '.sqlite';
$downloadPath = $tmpPath;

try {
    // Registra primeiro para que o próprio evento também faça parte da cópia.
    try { log_activity('database_backup', 'settings', null, 'Backup manual do SQLite'); } catch (Throwable $ignored) {}

    // VACUUM INTO gera uma cópia consistente, incluindo dados que estejam no WAL.
    // Em SQLite antigo ou hospedagem com limitação, há fallback seguro abaixo.
    try {
        @unlink($tmpPath);
        db()->exec('VACUUM INTO ' . db()->quote($tmpPath));
    } catch (Throwable $vacuumError) {
        try { db()->exec('PRAGMA wal_checkpoint(FULL)'); } catch (Throwable $ignored) {}
        clearstatcache(true, $dbPath);
        if (!@copy($dbPath, $tmpPath)) {
            throw new RuntimeException('Não foi possível preparar a cópia do banco para download.');
        }
    }

    clearstatcache(true, $downloadPath);
    if (!is_file($downloadPath) || !is_readable($downloadPath) || filesize($downloadPath) < 16) {
        throw new RuntimeException('O arquivo de backup não pôde ser preparado.');
    }

    // Confirma que o arquivo gerado realmente é SQLite antes de enviá-lo.
    $check = @fopen($downloadPath, 'rb');
    $signature = $check ? fread($check, 16) : '';
    if (is_resource($check)) fclose($check);
    if ($signature !== "SQLite format 3\000") {
        throw new RuntimeException('A cópia gerada não é um banco SQLite válido.');
    }

    $filename = 'linkexpress-backup-' . date('Y-m-d-His') . '.sqlite';
    $size = (int)filesize($downloadPath);

    // Libera o lock da sessão antes de começar a transferência.
    if (session_status() === PHP_SESSION_ACTIVE) @session_write_close();

    // Remove qualquer saída anterior para preservar o arquivo binário.
    while (ob_get_level() > 0) @ob_end_clean();

    header('Content-Type: application/vnd.sqlite3');
    header('Content-Disposition: attachment; filename="' . $filename . '"; filename*=UTF-8\'\'' . rawurlencode($filename));
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: ' . $size);
    header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('Expires: 0');
    header('X-Content-Type-Options: nosniff');

    $fp = @fopen($downloadPath, 'rb');
    if (!$fp) throw new RuntimeException('Não foi possível acessar o backup para download.');
    while (!feof($fp)) {
        $chunk = fread($fp, 1024 * 1024);
        if ($chunk === false) break;
        echo $chunk;
        if (function_exists('fastcgi_finish_request')) {
            // Não usa fastcgi_finish_request aqui: ele encerraria a resposta antes do arquivo completo.
        }
        @flush();
    }
    fclose($fp);
    @unlink($tmpPath);
    exit;
} catch (Throwable $e) {
    @unlink($tmpPath);
    while (ob_get_level() > 0) @ob_end_clean();
    @error_log('Backup download: ' . $e->getMessage());
    http_response_code(500);
    header('Content-Type: text/plain; charset=UTF-8');
    exit('Não foi possível gerar o backup. Verifique as permissões da pasta storage e tente novamente.');
}
