<?php
declare(strict_types=1);
require_once __DIR__ . '/app/Helpers/bootstrap.php';
$user = require_login();
if (($user['role'] ?? '') !== 'main_admin') { http_response_code(403); exit(t('updates.error_access')); }

$root = __DIR__;
$backupRoot = $root . '/storage/updates';
$error = null; $notice = null;
if (!is_dir($backupRoot) && !@mkdir($backupRoot, 0755, true)) { $error = t('update_manager.error_directory'); }

function triptale_update_backup_list(string $directory): array {
    $items = glob($directory . '/backup-*', GLOB_ONLYDIR) ?: [];
    rsort($items, SORT_STRING);
    return $items;
}
function triptale_update_safe_path(string $path): bool {
    return $path !== '' && !str_contains($path, '..') && !str_starts_with($path, '/') && !preg_match('#^(?:config/|storage/|uploads/|setup\.php)#i', $path);
}
function triptale_update_copy(string $from, string $to): bool {
    $folder = dirname($to);
    return (is_dir($folder) || @mkdir($folder, 0755, true)) && @copy($from, $to);
}
function triptale_update_delete_backup(string $directory): bool {
    if (!is_dir($directory)) { return false; }
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
    foreach ($iterator as $entry) {
        if ($entry->isLink() || $entry->isFile()) {
            if (!@unlink($entry->getPathname())) { return false; }
        } elseif (!@rmdir($entry->getPathname())) { return false; }
    }
    return @rmdir($directory);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && $error === null) {
    verify_csrf();
    $action = (string)($_POST['action'] ?? '');
    try {
        if ($action === 'delete_backup') {
            $name = basename((string)($_POST['backup'] ?? ''));
            $backupPath = $backupRoot . '/' . $name;
            if (!preg_match('/^backup-[0-9]{8}-[0-9]{6}$/', $name) || !is_file($backupPath . '/manifest.json')) { throw new RuntimeException(t('update_manager.error_backup')); }
            if (!triptale_update_delete_backup($backupPath)) { throw new RuntimeException(t('update_manager.error_delete')); }
            $notice = t('update_manager.deleted');
        } elseif ($action === 'restore') {
            $name = basename((string)($_POST['backup'] ?? ''));
            $manifestFile = $backupRoot . '/' . $name . '/manifest.json';
            if (!preg_match('/^backup-[0-9]{8}-[0-9]{6}$/', $name) || !is_file($manifestFile)) { throw new RuntimeException(t('update_manager.error_backup')); }
            $manifest = json_decode((string)file_get_contents($manifestFile), true);
            if (!is_array($manifest) || !is_array($manifest['files'] ?? null)) { throw new RuntimeException(t('update_manager.error_backup')); }
            foreach ($manifest['files'] as $file) {
                if (!is_string($file) || !triptale_update_safe_path($file)) { continue; }
                $source = $backupRoot . '/' . $name . '/files/' . $file;
                if (!empty($manifest['existed'][$file])) {
                    if (!is_file($source) || !triptale_update_copy($source, $root . '/' . $file)) { throw new RuntimeException(t('update_manager.error_restore')); }
                } elseif (is_file($root . '/' . $file) && !@unlink($root . '/' . $file)) { throw new RuntimeException(t('update_manager.error_restore')); }
            }
            $notice = t('update_manager.restored');
        } elseif ($action === 'install') {
            if (!class_exists('ZipArchive')) { throw new RuntimeException(t('update_manager.error_zip')); }
            $file = $_FILES['update_package'] ?? null;
            if (!is_array($file) || (int)($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK || empty($file['tmp_name'])) { throw new RuntimeException(t('update_manager.error_file')); }
            if ((int)($file['size'] ?? 0) > 30 * 1024 * 1024) { throw new RuntimeException(t('update_manager.error_size')); }
            $zip = new ZipArchive();
            if ($zip->open((string)$file['tmp_name']) !== true) { throw new RuntimeException(t('update_manager.error_zip')); }
            $manifestRaw = $zip->getFromName('triptale-update.json');
            $manifest = is_string($manifestRaw) ? json_decode($manifestRaw, true) : null;
            if (!is_array($manifest) || !is_array($manifest['files'] ?? null) || empty($manifest['version'])) { $zip->close(); throw new RuntimeException(t('update_manager.error_manifest')); }
            $files = [];
            foreach ($manifest['files'] as $path) {
                if (!is_string($path) || !triptale_update_safe_path($path) || $zip->locateName($path) === false) { $zip->close(); throw new RuntimeException(t('update_manager.error_manifest')); }
                $files[] = $path;
            }
            if (!$files) { $zip->close(); throw new RuntimeException(t('update_manager.error_manifest')); }
            $sqlFiles = is_array($manifest['database_files'] ?? null) ? $manifest['database_files'] : [];
            foreach ($sqlFiles as $path) {
                if (!is_string($path) || !preg_match('#^database/updates/[A-Za-z0-9_.-]+\.sql$#', $path) || $zip->locateName($path) === false) { $zip->close(); throw new RuntimeException(t('update_manager.error_manifest')); }
            }
            $backupName = 'backup-' . date('Ymd-His');
            $backupPath = $backupRoot . '/' . $backupName;
            if (!@mkdir($backupPath . '/files', 0755, true)) { $zip->close(); throw new RuntimeException(t('update_manager.error_directory')); }
            $existed = [];
            foreach ($files as $path) {
                $existed[$path] = is_file($root . '/' . $path);
                if ($existed[$path] && !triptale_update_copy($root . '/' . $path, $backupPath . '/files/' . $path)) { $zip->close(); throw new RuntimeException(t('update_manager.error_backup_create')); }
            }
            file_put_contents($backupPath . '/manifest.json', json_encode(['version' => (string)$manifest['version'], 'files' => $files, 'existed' => $existed, 'created_at' => date(DATE_ATOM)], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
            if ($sqlFiles) {
                $pdo = db(); $pdo->beginTransaction();
                try {
                    $prefix = (string)(app_config()['database']['prefix'] ?? '');
                    foreach ($sqlFiles as $path) {
                        $sql = $zip->getFromName($path);
                        if (!is_string($sql)) { throw new RuntimeException(t('update_manager.error_database')); }
                        $sql = str_replace(['{{PREFIX}}', 'travel_1_'], $prefix, $sql);
                        foreach (preg_split('/;\s*(?=(?:CREATE|ALTER|INSERT|UPDATE|DELETE|SET)\b)/i', $sql) ?: [] as $statement) {
                            if (trim($statement) !== '') { $pdo->exec($statement); }
                        }
                    }
                    if ($pdo->inTransaction()) { $pdo->commit(); }
                } catch (Throwable $exception) {
                    if ($pdo->inTransaction()) { $pdo->rollBack(); }
                    throw new RuntimeException(t('update_manager.error_database'));
                }
            }
            foreach ($files as $path) {
                $stream = $zip->getStream($path);
                if (!is_resource($stream)) { $zip->close(); throw new RuntimeException(t('update_manager.error_install')); }
                $target = $root . '/' . $path; $folder = dirname($target);
                if ((!is_dir($folder) && !@mkdir($folder, 0755, true)) || !@file_put_contents($target, stream_get_contents($stream))) { fclose($stream); $zip->close(); throw new RuntimeException(t('update_manager.error_install')); }
                fclose($stream);
            }
            $zip->close();
            $backups = triptale_update_backup_list($backupRoot);
            foreach (array_slice($backups, 3) as $old) { $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($old, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST); foreach ($iterator as $entry) { $entry->isDir() ? @rmdir($entry->getPathname()) : @unlink($entry->getPathname()); } @rmdir($old); }
            $notice = str_replace('{version}', (string)$manifest['version'], t('update_manager.installed'));
        }
    } catch (Throwable $exception) { $error = $exception->getMessage(); }
}
$backups = is_dir($backupRoot) ? triptale_update_backup_list($backupRoot) : [];
$installedManifest = is_file(__DIR__ . '/database/install-manifest.php') ? require __DIR__ . '/database/install-manifest.php' : [];
$installedVersion = is_array($installedManifest) ? (string)($installedManifest['version'] ?? '–') : '–';
?>
<!doctype html><html lang="<?= htmlspecialchars(language(), ENT_QUOTES, 'UTF-8') ?>"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title><?= htmlspecialchars(t('update_manager.title'), ENT_QUOTES, 'UTF-8') ?> · TripTale</title><style>body{font-family:system-ui,sans-serif;background:#f4f6f8;margin:0;padding:32px;color:#17202a}main{max-width:720px;margin:auto;background:#fff;padding:28px;border-radius:14px;box-shadow:0 4px 18px #0001}.button,button{display:inline-block;padding:10px 13px;background:#126e82;color:#fff;border:0;border-radius:7px;text-decoration:none;font:inherit;font-weight:700;cursor:pointer}.back{background:#e3ebee;color:#126e82}.version{display:inline-block;margin:4px 0 18px;padding:7px 11px;border:1px solid #b9c1c8;border-radius:999px;background:#eef5f8}.panel{margin:18px 0;padding:18px;border:1px solid #d6e0e4;border-left:4px solid #75aeb9;border-radius:10px}input{box-sizing:border-box;width:100%;padding:10px;margin:8px 0;border:1px solid #b9c1c8;border-radius:7px;font:inherit}.hint{color:#59636d}.error,.success{padding:12px;border-radius:8px}.error{background:#ffe6e3}.success{background:#e6f5e9}.backup-actions{display:flex;flex-wrap:wrap;gap:8px}.delete-backup{background:#9d2d2d}@media(max-width:600px){body{padding:12px}main{padding:16px}}</style></head><body><main><a class="button back" href="/settings.php"><?= htmlspecialchars(t('settings.back'), ENT_QUOTES, 'UTF-8') ?></a><h1><?= htmlspecialchars(t('update_manager.title'), ENT_QUOTES, 'UTF-8') ?></h1><p class="hint"><?= htmlspecialchars(t('update_manager.intro'), ENT_QUOTES, 'UTF-8') ?></p><p class="version"><strong><?= htmlspecialchars(t('updates.version_current'), ENT_QUOTES, 'UTF-8') ?></strong> <?= htmlspecialchars($installedVersion, ENT_QUOTES, 'UTF-8') ?></p><?php if ($notice): ?><p class="success"><?= htmlspecialchars($notice, ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?><?php if ($error): ?><p class="error"><?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?><section class="panel"><h2><?= htmlspecialchars(t('update_manager.install_title'), ENT_QUOTES, 'UTF-8') ?></h2><p class="hint"><?= htmlspecialchars(t('update_manager.install_hint'), ENT_QUOTES, 'UTF-8') ?></p><form method="post" enctype="multipart/form-data"><input type="hidden" name="csrf_token" value="<?= htmlspecialchars(csrf_token(), ENT_QUOTES, 'UTF-8') ?>"><input type="file" name="update_package" accept=".zip,application/zip" required><button name="action" value="install"><?= htmlspecialchars(t('update_manager.install'), ENT_QUOTES, 'UTF-8') ?></button></form></section><section class="panel"><h2><?= htmlspecialchars(t('update_manager.backups_title'), ENT_QUOTES, 'UTF-8') ?></h2><p class="hint"><?= htmlspecialchars(t('update_manager.backups_hint'), ENT_QUOTES, 'UTF-8') ?></p><?php if (!$backups): ?><p><?= htmlspecialchars(t('update_manager.backups_empty'), ENT_QUOTES, 'UTF-8') ?></p><?php endif; ?><?php foreach ($backups as $backup): $manifest = json_decode((string)@file_get_contents($backup . '/manifest.json'), true) ?: []; ?><form method="post"><input type="hidden" name="csrf_token" value="<?= htmlspecialchars(csrf_token(), ENT_QUOTES, 'UTF-8') ?>"><input type="hidden" name="backup" value="<?= htmlspecialchars(basename($backup), ENT_QUOTES, 'UTF-8') ?>"><p><strong><?= htmlspecialchars((string)($manifest['version'] ?? basename($backup)), ENT_QUOTES, 'UTF-8') ?></strong><br><span class="hint"><?= htmlspecialchars((string)($manifest['created_at'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span></p><div class="backup-actions"><button class="restore" name="action" value="restore"><?= htmlspecialchars(t('update_manager.restore'), ENT_QUOTES, 'UTF-8') ?></button><button class="delete-backup" name="action" value="delete_backup" onclick="return confirm(<?= htmlspecialchars(json_encode(t('update_manager.delete_confirm'), JSON_UNESCAPED_UNICODE), ENT_QUOTES, 'UTF-8') ?>)"><?= htmlspecialchars(t('update_manager.delete_backup'), ENT_QUOTES, 'UTF-8') ?></button></div></form><?php endforeach; ?></section></main></body></html>
