<?php
/**
 * Update-Funktion: Versionen vom Update-Server laden und installieren.
 * Nur nach Debug-Login erreichbar.
 */
require __DIR__ . '/config.php';
require __DIR__ . '/debug/auth.php';

requireLogin();

$updateServer = defined('UPDATE_SERVER') ? UPDATE_SERVER : 'https://update.piclock.cylone-network.com';
$installDir   = __DIR__;
$tmpZip       = __DIR__ . '/update.zip';
$backupDir    = __DIR__ . '/backup_' . date('YmdHis');

/** HTTP-Context mit Timeout für file_get_contents */
function httpContext($timeout = 30) {
    return stream_context_create([
        'http' => ['timeout' => $timeout],
        'ssl'  => ['verify_peer' => true]
    ]);
}

/** Prüft, ob alle ZIP-Einträge unter einem gemeinsamen Oberordner liegen; gibt dessen Prefix zurück oder null */
function zipGetSingleRootPrefix(ZipArchive $zip) {
    $n = $zip->numFiles;
    if ($n === 0) return null;
    $first = $zip->getNameIndex(0);
    if ($first === false) return null;
    $slash = strpos($first, '/');
    if ($slash === false) return null;
    $prefix = substr($first, 0, $slash + 1);
    for ($i = 1; $i < $n; $i++) {
        $name = $zip->getNameIndex($i);
        if ($name === false || strpos($name, $prefix) !== 0) return null;
    }
    return $prefix;
}

/** Kopiert Verzeichnis rekursiv nach $dst (überschreibt vorhandene Dateien) */
function copyMergeDir($src, $dst) {
    if (!is_dir($src)) return;
    @mkdir($dst, 0755, true);
    $dir = opendir($src);
    while (($f = readdir($dir)) !== false) {
        if ($f === '.' || $f === '..') continue;
        $srcPath = $src . '/' . $f;
        $dstPath = $dst . '/' . $f;
        if (is_dir($srcPath)) {
            copyMergeDir($srcPath, $dstPath);
        } else {
            @copy($srcPath, $dstPath);
        }
    }
    closedir($dir);
}

function rrmdir($dir) {
    if (!is_dir($dir)) return;
    $files = scandir($dir);
    foreach ($files as $file) {
        if ($file !== '.' && $file !== '..') {
            $path = $dir . '/' . $file;
            if (is_dir($path)) rrmdir($path);
            else @unlink($path);
        }
    }
    @rmdir($dir);
}

// ==============================
// VERSIONEN HOLEN (JSON)
// ==============================

if (isset($_GET['list'])) {
    $url = rtrim($updateServer, '/') . '/versions.php';
    $json = @file_get_contents($url, false, httpContext(15));
    header('Content-Type: application/json');
    echo $json !== false ? $json : json_encode(['error' => 'Versionen konnten nicht geladen werden']);
    exit;
}

// ==============================
// UPDATE AUSFÜHREN
// ==============================

if (isset($_GET['install'])) {
    header('Content-Type: text/plain; charset=utf-8');

    $version = $_GET['install'];
    if (!preg_match('/^[a-zA-Z0-9._-]+$/', $version)) {
        exit('Ungültige Version');
    }

    $url = rtrim($updateServer, '/') . '/versions.php';
    $json = @file_get_contents($url, false, httpContext(15));
    if ($json === false) {
        exit('Versionen konnten nicht geladen werden');
    }

    $versions = json_decode($json, true);
    if (!is_array($versions)) {
        exit('Ungültige Antwort vom Update-Server');
    }

    $selected = null;
    foreach ($versions as $v) {
        if (isset($v['version']) && $v['version'] === $version) {
            $selected = $v;
            break;
        }
    }

    if (!$selected || empty($selected['file'])) {
        exit('Version nicht gefunden');
    }

    $downloadUrl = rtrim($updateServer, '/') . '/releases/' . $selected['file'];

    $zipData = @file_get_contents($downloadUrl, false, httpContext(120));
    if ($zipData === false || $zipData === '') {
        exit('Download fehlgeschlagen');
    }

    if (file_put_contents($tmpZip, $zipData) === false) {
        exit('ZIP konnte nicht gespeichert werden');
    }
    unset($zipData);

    if (!file_exists($tmpZip) || filesize($tmpZip) === 0) {
        exit('Download fehlgeschlagen');
    }

    echo "Backup...\n";
    flush();
    if (!@mkdir($backupDir, 0755, true)) {
        exit('Backup-Ordner konnte nicht erstellt werden');
    }

    if (PHP_OS_FAMILY !== 'Windows') {
        exec('cp -r ' . escapeshellarg($installDir) . '/* ' . escapeshellarg($backupDir) . ' 2>/dev/null');
    } else {
        foreach (scandir($installDir) as $f) {
            if ($f === '.' || $f === '..') continue;
            $src = $installDir . '/' . $f;
            $dst = $backupDir . '/' . $f;
            if (is_dir($src)) {
                @mkdir($dst);
            } else {
                @copy($src, $dst);
            }
        }
    }

    echo "Übernahme der Änderungen (bestehende Dateien werden überschrieben)...\n";
    flush();

    $zip = new ZipArchive();
    if ($zip->open($tmpZip) !== true) {
        exit('ZIP Fehler');
    }

    $numFiles = $zip->numFiles;
    $prefix = '';
    $segmentCounts = [];
    for ($i = 0; $i < $numFiles; $i++) {
        $entryName = $zip->getNameIndex($i);
        if (strpos($entryName, '/') !== false) {
            $seg = substr($entryName, 0, strpos($entryName, '/') + 1);
            $segmentCounts[$seg] = ($segmentCounts[$seg] ?? 0) + 1;
        }
    }
    if (!empty($segmentCounts)) {
        arsort($segmentCounts);
        $prefix = key($segmentCounts);
    }

    $selfPath = str_replace('\\', '/', $installDir . '/update.php');
    $pendingSelfUpdate = null;

    for ($i = 0; $i < $numFiles; $i++) {
        $entryName = $zip->getNameIndex($i);
        if ($prefix !== '' && strpos($entryName, $prefix) !== 0) {
            continue;
        }
        $relativePath = $prefix !== '' ? substr($entryName, strlen($prefix)) : $entryName;
        if ($relativePath === '' || $relativePath === false) {
            continue;
        }
        $targetPath = $installDir . '/' . $relativePath;
        $isSelf = (str_replace('\\', '/', $targetPath) === $selfPath);

        if (substr($entryName, -1) === '/') {
            if (!is_dir($targetPath)) {
                @mkdir($targetPath, 0755, true);
            }
        } else {
            $targetDir = dirname($targetPath);
            if (!is_dir($targetDir)) {
                @mkdir($targetDir, 0755, true);
            }
            $content = $zip->getFromIndex($i);
            if ($content !== false) {
                if ($isSelf) {
                    $pendingSelfUpdate = $installDir . '/update.php.new';
                    file_put_contents($pendingSelfUpdate, $content);
                } else {
                    file_put_contents($targetPath, $content);
                }
            }
        }
    }
    $zip->close();
    @unlink($tmpZip);

    if ($pendingSelfUpdate !== null && file_exists($pendingSelfUpdate)) {
        @rename($pendingSelfUpdate, $installDir . '/update.php');
    }

    $dbResult = require $installDir . '/run_database_updates.inc.php';
    if ($dbResult['executed'] > 0) {
        echo "DB-Updates: " . $dbResult['executed'] . " ausgeführt\n";
    }
    if (!empty($dbResult['errors'])) {
        echo "DB-Fehler: " . implode("; ", $dbResult['errors']) . "\n";
    }

    echo "UPDATE SUCCESS\n";
    exit;
}

// ==============================
// UI
// ==============================

$url = rtrim($updateServer, '/') . '/versions.php';
$json = @file_get_contents($url, false, httpContext(15));
$versions = is_string($json) ? json_decode($json, true) : [];
if (!is_array($versions)) {
    $versions = [];
}
// Neueste zuerst: nach Version sortieren (fallback: nach Datum/Reihenfolge)
usort($versions, function ($a, $b) {
    $va = $a['version'] ?? '';
    $vb = $b['version'] ?? '';
    if ($va === $vb) return 0;
    if (preg_match('/^\d+\.\d+/', $va) && preg_match('/^\d+\.\d+/', $vb)) {
        return version_compare($vb, $va);
    }
    if (!empty($a['date']) && !empty($b['date'])) {
        return strcmp($b['date'], $a['date']);
    }
    return 0;
});
$versions = array_slice($versions, 0, 5);
?>
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8">
    <title>Update</title>
    <style>
        body { font-family: system-ui, sans-serif; margin: 20px; background: #0f1115; color: #e6e6e6; }
        a { color: #4da3ff; }
        .error { color: #ff6b6b; }
        .update-form { margin: 16px 0; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
        .update-form select { padding: 8px 12px; min-width: 200px; background: #1c2130; border: 1px solid #2a2f3a; color: #e6e6e6; font-size: 1rem; }
        .update-form button { padding: 8px 16px; background: #4da3ff; border: none; color: #0f1115; cursor: pointer; font-weight: bold; }
        .update-form button:hover { background: #6bb3ff; }
        .version-hint { font-size: 0.9rem; opacity: 0.8; margin-top: 4px; }

        #updateOverlay { position: fixed; inset: 0; background: rgba(15,17,21,0.95); display: none; align-items: center; justify-content: center; flex-direction: column; gap: 24px; z-index: 9999; }
        #updateOverlay.visible { display: flex; }
        .update-spinner { width: 48px; height: 48px; border: 4px solid #2a2f3a; border-top-color: #4da3ff; border-radius: 50%; animation: update-spin 0.9s linear infinite; }
        .update-spinner.done { animation: none; border-color: #22c55e; border-top-color: #22c55e; }
        .update-status { font-size: 1.1rem; color: #e6e6e6; }
        .update-log { max-width: 90%; max-height: 120px; overflow: auto; background: #1c2130; padding: 12px; border-radius: 8px; font-size: 12px; font-family: monospace; white-space: pre-wrap; text-align: left; }
        @keyframes update-spin { to { transform: rotate(360deg); } }
    </style>
</head>
<body>
<h1>Update-System</h1>
<?php if (empty($versions)): ?>
    <p class="error">Keine Versionen geladen. Prüfe UPDATE_SERVER in config.php und ob versions.php erreichbar ist.</p>
<?php else: ?>
<div id="updateOverlay">
    <div class="update-spinner" id="updateSpinner"></div>
    <p class="update-status" id="updateStatus">Update wird installiert…</p>
    <pre class="update-log" id="updateLog"></pre>
    <a href="update.php" id="updateBackLink" style="display:none;">← Zurück zum Update</a>
</div>

<form class="update-form" method="get" action="update.php" id="updateForm">
    <label for="versionSelect">Version:</label>
    <select id="versionSelect" name="install" required>
        <?php foreach ($versions as $i => $v): ?>
            <option value="<?= htmlspecialchars($v['version'] ?? '') ?>" data-commit="<?= htmlspecialchars($v['commit'] ?? '') ?>"<?= $i === 0 ? ' selected' : '' ?>>
                <?= htmlspecialchars($v['version'] ?? '-') ?>
            </option>
        <?php endforeach; ?>
    </select>
    <button type="submit">Installieren</button>
</form>
<p class="version-hint" id="versionHint"></p>
<script>
(function() {
    var sel = document.getElementById('versionSelect');
    var hint = document.getElementById('versionHint');
    function updateHint() {
        var opt = sel.options[sel.selectedIndex];
        var commit = opt && opt.getAttribute('data-commit');
        hint.textContent = commit ? 'Commit: ' + commit : 'Neueste Version (oben).';
    }
    sel.addEventListener('change', updateHint);
    updateHint();

    var form = document.getElementById('updateForm');
    var overlay = document.getElementById('updateOverlay');
    var spinner = document.getElementById('updateSpinner');
    var status = document.getElementById('updateStatus');
    var logEl = document.getElementById('updateLog');
    var backLink = document.getElementById('updateBackLink');

    form.addEventListener('submit', function(e) {
        e.preventDefault();
        var version = sel.value;
        if (!version) return;
        overlay.classList.add('visible');
        logEl.textContent = '';
        spinner.classList.remove('done');
        backLink.style.display = 'none';
        status.textContent = 'Update wird installiert…';

        fetch('update.php?install=' + encodeURIComponent(version))
            .then(function(r) { return r.text(); })
            .then(function(text) {
                logEl.textContent = text;
                logEl.scrollTop = logEl.scrollHeight;
                if (text.indexOf('UPDATE SUCCESS') !== -1) {
                    spinner.classList.add('done');
                    spinner.style.display = 'none';
                    status.textContent = 'Update erfolgreich! Seite wird neu geladen…';
                    setTimeout(function() { location.reload(); }, 1800);
                } else {
                    status.textContent = 'Fertig (prüfe Ausgabe)';
                    backLink.style.display = 'inline';
                }
            })
            .catch(function(err) {
                logEl.textContent = 'Fehler: ' + err.message;
                status.textContent = 'Fehler beim Update';
                backLink.style.display = 'inline';
            });
    });
})();
</script>
<?php endif; ?>
</body>
</html>
