<?php
declare(strict_types=1);
require __DIR__ . '/lib/Auth.php';
require __DIR__ . '/lib/Layout.php';

use PSLauncher\Admin\Auth;
use PSLauncher\Admin\Layout;

Auth::requireLogin();
$config = require __DIR__ . '/../api/config.php';

$root         = dirname(__DIR__);
$manifestPath = "$root/manifest/versions.json";
$buildsDir    = "$root/builds";
$notesDir     = "$root/releasenotes";

$message = null; $messageType = 'success';

function loadManifest(string $path): array
{
    if (!is_file($path)) return ['schemaVersion' => 1, 'product' => 'PROSERVE_UE', 'latest' => null, 'versions' => []];
    return json_decode(file_get_contents($path), true) ?? [];
}

function saveManifest(string $path, array $manifest): void
{
    // Re-tri par SemVer descendant
    usort($manifest['versions'], fn($a, $b) => version_compare($b['version'], $a['version']));
    file_put_contents(
        $path,
        json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"
    );
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    Auth::checkCsrf();
    $action = $_POST['action'] ?? '';
    $manifest = loadManifest($manifestPath);

    try {
        if ($action === 'add') {
            $version    = trim($_POST['version'] ?? '');
            $minLicDate = trim($_POST['min_license_date'] ?? '');
            $releasedAt = trim($_POST['released_at'] ?? '');
            $notes      = $_POST['notes'] ?? '';
            $available  = isset($_POST['available']);

            if (!preg_match('/^\d+\.\d+\.\d+$/', $version)) {
                throw new Exception('Numéro de version invalide (format X.Y.Z attendu).');
            }
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $minLicDate)) {
                throw new Exception('Date min_license_date invalide.');
            }
            foreach ($manifest['versions'] as $v) {
                if ($v['version'] === $version) throw new Exception("v{$version} existe déjà dans le manifest.");
            }

            $releasedAtIso = $releasedAt !== ''
                ? (new DateTime($releasedAt, new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s\Z')
                : (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s\Z');

            $base = $config['base_url'] ?? 'https://asterionvr.com/PS_Launcher';
            $manifest['versions'][] = [
                'version'               => $version,
                'releasedAt'            => $releasedAtIso,
                'executable'            => 'PROSERVE_UE_5_5.exe',
                'installFolderTemplate' => 'PROSERVE v{version}',
                'download'              => [
                    'url'       => "{$base}/builds/proserve-{$version}.zip",
                    'sizeBytes' => 0,
                    'sha256'    => 'REPLACE_AFTER_BUILD',
                ],
                'releaseNotesUrl'       => "{$base}/api/releasenotes/{$version}",
                'minLicenseDate'        => $minLicDate,
                'availableForDownload'  => $available,
            ];

            saveManifest($manifestPath, $manifest);

            if ($notes !== '') {
                if (!is_dir($notesDir)) mkdir($notesDir, 0755, true);
                file_put_contents("$notesDir/{$version}.md", $notes);
            }
            $message = "v{$version} ajoutée. Upload le ZIP en SFTP dans builds/, puis clique « Sync (sign-manifest) ».";
        }
        elseif ($action === 'edit_notes') {
            $version = $_POST['version'] ?? '';
            $notes   = $_POST['notes'] ?? '';
            if (!preg_match('/^\d+\.\d+\.\d+$/', $version)) throw new Exception('Version invalide.');
            if (!is_dir($notesDir)) mkdir($notesDir, 0755, true);
            file_put_contents("$notesDir/{$version}.md", $notes);
            $message = "Release notes de v{$version} mises à jour.";
        }
        elseif ($action === 'edit_meta') {
            $version    = $_POST['version'] ?? '';
            $minLicDate = trim($_POST['min_license_date'] ?? '');
            $releasedAt = trim($_POST['released_at'] ?? '');
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $minLicDate)) {
                throw new Exception('min_license_date invalide.');
            }
            foreach ($manifest['versions'] as &$v) {
                if ($v['version'] === $version) {
                    $v['minLicenseDate'] = $minLicDate;
                    if ($releasedAt !== '') {
                        $v['releasedAt'] = (new DateTime($releasedAt, new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s\Z');
                    }
                    break;
                }
            }
            unset($v);
            saveManifest($manifestPath, $manifest);
            $message = "Méta de v{$version} mises à jour.";
        }
        elseif ($action === 'toggle_available') {
            $version = $_POST['version'] ?? '';
            foreach ($manifest['versions'] as &$v) {
                if ($v['version'] === $version) {
                    $v['availableForDownload'] = !($v['availableForDownload'] ?? true);
                    break;
                }
            }
            unset($v);
            saveManifest($manifestPath, $manifest);
            $message = "Disponibilité de v{$version} mise à jour.";
        }
        elseif ($action === 'delete') {
            $version = $_POST['version'] ?? '';
            $manifest['versions'] = array_values(array_filter(
                $manifest['versions'],
                fn($v) => $v['version'] !== $version
            ));
            saveManifest($manifestPath, $manifest);
            $message = "v{$version} retirée du manifest. Le ZIP reste dans builds/ (à supprimer en SFTP si voulu).";
        }
        elseif ($action === 'sync' || $action === 'sync_versions') {
            require_once "$root/tools/SignManifest.php";
            $signer = new \PSLauncher\Tools\SignManifest($root);
            $scope = $action === 'sync_versions' ? 'versions' : 'all';
            $force = !empty($_POST['force']);
            $result = $signer->run($scope, $force);
            $label = $scope === 'versions' ? 'versions Proserve' : 'manifest complet';
            $forceLabel = $force ? ' [FORCE: cache ignoré]' : ' [cache utilisé pour les ZIPs inchangés]';
            $message = "Sortie du script (scope: {$label}{$forceLabel}) :\n" . implode("\n", $result['log']);
            if (!$result['ok']) $messageType = 'error';
        }
        elseif ($action === 'sync_one') {
            $version = $_POST['version'] ?? '';
            if ($version === '') throw new Exception("Version manquante");
            require_once "$root/tools/SignManifest.php";
            $signer = new \PSLauncher\Tools\SignManifest($root);
            $force = !empty($_POST['force']);
            $result = $signer->run('versions', $force, $version);
            $forceLabel = $force ? ' [FORCE]' : '';
            $message = "Hash de v{$version}{$forceLabel} :\n" . implode("\n", $result['log']);
            if (!$result['ok']) $messageType = 'error';
        }
        elseif ($action === 'set_skip_hash') {
            $version  = $_POST['version'] ?? '';
            $skipHash = !empty($_POST['skip']);
            foreach ($manifest['versions'] as &$v) {
                if ($v['version'] === $version) {
                    if ($skipHash) {
                        $v['download']['hashAlgorithm'] = 'none';
                        $v['download']['sha256'] = '';
                    } else {
                        unset($v['download']['hashAlgorithm']);
                        if (empty($v['download']['sha256'])) {
                            $v['download']['sha256'] = 'REPLACE_AFTER_BUILD';
                        }
                    }
                    break;
                }
            }
            unset($v);
            saveManifest($manifestPath, $manifest);

            // Re-signature auto pour garder le manifest valide après le changement.
            // En mode skip, on n'a rien à hasher : on appelle run() qui se contentera
            // de détecter hashAlgorithm=none et re-signera. Pas de calcul lourd.
            require_once "$root/tools/SignManifest.php";
            $signer = new \PSLauncher\Tools\SignManifest($root);
            $resign = $signer->run('versions', false);
            $message = ($skipHash
                ? "Vérif SHA-256 désactivée pour v{$version} et manifest re-signé."
                : "Vérif SHA-256 réactivée pour v{$version}. Clique « 🔁 Hash » sur cette ligne pour calculer le SHA-256.")
                . "\n" . implode("\n", $resign['log']);
            if (!$resign['ok']) $messageType = 'error';
        }
    } catch (Exception $e) {
        $message = $e->getMessage();
        $messageType = 'error';
    }
}

$manifest  = loadManifest($manifestPath);
$zips      = is_dir($buildsDir) ? array_map('basename', glob("$buildsDir/*.zip") ?: []) : [];
$zipSizes  = [];
foreach ($zips as $z) $zipSizes[$z] = filesize("$buildsDir/$z");

// Pour l'édition : pré-charge les release notes existantes
$existingNotes = [];
foreach ($manifest['versions'] ?? [] as $v) {
    $f = "$notesDir/{$v['version']}.md";
    $existingNotes[$v['version']] = is_file($f) ? file_get_contents($f) : '';
}

Layout::header('Versions', 'versions');
?>
<h1>Versions</h1>

<?php Layout::flash($message, $messageType); ?>

<div class="card">
    <h2>Workflow d'une nouvelle release</h2>
    <ol class="muted">
        <li>Ajoute l'entrée du manifest avec le formulaire ci-dessous (version, date min de license, release notes).</li>
        <li>Upload le ZIP correspondant via SFTP dans <code>www/PS_Launcher/builds/</code> en respectant le nom <code>proserve-{version}.zip</code>.</li>
        <li>Clique <strong>🔁 Sync (sign-manifest)</strong> pour calculer le SHA-256, mettre à jour <code>sizeBytes</code>, bumper <code>latest</code>, et signer le manifest avec Ed25519.</li>
        <li>Les clients PS_Launcher détecteront la nouvelle version au prochain « Vérifier les MAJ ».</li>
    </ol>
</div>

<div class="card">
    <h2>Ajouter une version</h2>
    <form method="post">
        <?= Layout::csrfField() ?>
        <input type="hidden" name="action" value="add">
        <div class="row">
            <div class="col field">
                <label>Version (X.Y.Z)</label>
                <input type="text" name="version" placeholder="1.4.8" required pattern="\d+\.\d+\.\d+">
            </div>
            <div class="col field">
                <label>Date de release</label>
                <input type="datetime-local" name="released_at">
            </div>
            <div class="col field">
                <label>min_license_date (license requise)</label>
                <input type="date" name="min_license_date" required>
            </div>
        </div>
        <div class="field">
            <label>Release notes (Markdown)</label>
            <textarea name="notes" rows="8" placeholder="# Proserve v1.4.8&#10;&#10;## Nouveautés&#10;- ..." style="font-family: 'Cascadia Code', Consolas, monospace;"></textarea>
        </div>
        <div class="field">
            <label><input type="checkbox" name="available" checked> Disponible au téléchargement</label>
        </div>
        <button class="btn btn-success" type="submit">Ajouter au manifest</button>
    </form>
</div>

<div class="card">
    <div class="toolbar">
        <h2 style="margin: 0; flex: 1;">Manifest actuel</h2>
        <span class="muted">
            Signature :
            <?= empty($manifest['signature'])
                ? "<span class='badge badge-warning'>NON SIGNÉ</span>"
                : "<span class='badge badge-success'>SIGNÉ Ed25519</span>" ?>
            <?php if (!empty($manifest['latest'])): ?>
                • latest : <code>v<?= htmlspecialchars($manifest['latest']) ?></code>
            <?php endif; ?>
        </span>
        <form method="post" style="display:inline">
            <?= Layout::csrfField() ?>
            <input type="hidden" name="action" value="sync_versions">
            <button class="btn btn-primary" type="submit">🔁 Hasher les versions + signer</button>
            <label style="margin-left:12px;font-size:12px;color:#888" title="Recalcule TOUS les SHA-256 même si les ZIPs n'ont pas changé. Lent. Cocher seulement en cas de doute sur le cache.">
                <input type="checkbox" name="force" value="1"> Force re-hash
            </label>
        </form>
    </div>

    <table>
        <thead>
            <tr>
                <th>Version</th>
                <th>Release</th>
                <th>min_license</th>
                <th>ZIP</th>
                <th>Hash</th>
                <th>Visible</th>
                <th style="text-align:right">Actions</th>
            </tr>
        </thead>
        <tbody>
        <?php foreach ($manifest['versions'] ?? [] as $v):
            $zipBase    = basename(parse_url($v['download']['url'], PHP_URL_PATH) ?? '');
            $zipExists  = in_array($zipBase, $zips, true);
            $hashed     = !empty($v['download']['sha256']) && !str_starts_with($v['download']['sha256'], 'REPLACE');
            $hashSkipped = strtolower((string)($v['download']['hashAlgorithm'] ?? 'sha256')) === 'none';
        ?>
            <tr>
                <td><strong>v<?= htmlspecialchars($v['version']) ?></strong></td>
                <td class="muted"><?= htmlspecialchars(substr($v['releasedAt'] ?? '', 0, 10)) ?></td>
                <td class="muted"><?= htmlspecialchars($v['minLicenseDate'] ?? '—') ?></td>
                <td>
                    <?php if ($zipExists): ?>
                        <span class="badge badge-success">présent</span>
                        <span class="muted"><?= Layout::formatBytes($zipSizes[$zipBase] ?? 0) ?></span>
                    <?php else: ?>
                        <span class="badge badge-warning">absent</span>
                        <div class="muted" style="font-size: 11px;">attendu : <code><?= htmlspecialchars($zipBase) ?></code></div>
                    <?php endif; ?>
                </td>
                <td>
                    <?php if ($hashSkipped): ?>
                        <span class="badge badge-warning" title="hashAlgorithm=none : la vérif SHA-256 est désactivée pour cette release">SKIP</span>
                    <?php elseif ($hashed): ?>
                        <code title="<?= htmlspecialchars($v['download']['sha256']) ?>"><?= substr($v['download']['sha256'], 0, 12) ?>…</code>
                    <?php else: ?>
                        <span class="badge badge-warning">à calculer</span>
                    <?php endif; ?>
                </td>
                <td>
                    <form method="post" style="display:inline">
                        <?= Layout::csrfField() ?>
                        <input type="hidden" name="action" value="toggle_available">
                        <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                        <button class="btn btn-secondary" type="submit">
                            <?= ($v['availableForDownload'] ?? true) ? '✓' : '✗' ?>
                        </button>
                    </form>
                </td>
                <td style="text-align: right; white-space: nowrap;">
                    <?php if ($zipExists && !$hashSkipped): ?>
                    <form method="post" style="display:inline" title="Recalcule le SHA-256 de cette version uniquement (utilise le cache si le ZIP n'a pas changé)">
                        <?= Layout::csrfField() ?>
                        <input type="hidden" name="action" value="sync_one">
                        <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                        <button class="btn btn-secondary" type="submit">🔁 Hash</button>
                    </form>
                    <?php endif; ?>
                    <details style="display: inline-block; margin: 0 4px;">
                        <summary class="btn btn-secondary">⚙ Hash</summary>
                        <div style="margin-top: 8px; min-width: 280px;">
                            <p class="muted" style="font-size: 12px; margin: 0 0 8px;">
                                <?php if ($hashSkipped): ?>
                                    La vérif SHA-256 est <strong>désactivée</strong> pour cette release.
                                    Le manifest sert <code>hashAlgorithm: "none"</code>. La sécurité repose
                                    uniquement sur la signature Ed25519 du manifest + HTTPS.
                                <?php else: ?>
                                    Désactive la vérif SHA-256 chez les clients pour cette release
                                    (utile sur très gros builds où on accepte le compromis perf/sécurité).
                                <?php endif; ?>
                            </p>
                            <form method="post" style="display:inline">
                                <?= Layout::csrfField() ?>
                                <input type="hidden" name="action" value="set_skip_hash">
                                <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                                <input type="hidden" name="skip" value="<?= $hashSkipped ? '0' : '1' ?>">
                                <button class="btn <?= $hashSkipped ? 'btn-success' : 'btn-warning' ?>" type="submit">
                                    <?= $hashSkipped ? '✓ Réactiver la vérif' : '⏭ Désactiver la vérif' ?>
                                </button>
                            </form>
                            <?php if ($zipExists): ?>
                            <form method="post" style="margin-top: 8px;" title="Recalcule en ignorant le cache même si le ZIP n'a pas changé">
                                <?= Layout::csrfField() ?>
                                <input type="hidden" name="action" value="sync_one">
                                <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                                <input type="hidden" name="force" value="1">
                                <button class="btn btn-secondary" type="submit">🔄 Force re-hash</button>
                            </form>
                            <?php endif; ?>
                        </div>
                    </details>
                    <details style="display: inline-block; margin: 0 4px;">
                        <summary class="btn btn-secondary">Méta</summary>
                        <form method="post" style="margin-top: 8px;">
                            <?= Layout::csrfField() ?>
                            <input type="hidden" name="action" value="edit_meta">
                            <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                            <div class="field">
                                <label>min_license_date</label>
                                <input type="date" name="min_license_date" value="<?= htmlspecialchars($v['minLicenseDate'] ?? '') ?>" required>
                            </div>
                            <div class="field">
                                <label>Date de release (UTC)</label>
                                <input type="datetime-local" name="released_at" value="<?= htmlspecialchars(substr($v['releasedAt'] ?? '', 0, 16)) ?>">
                            </div>
                            <button class="btn btn-primary" type="submit">Enregistrer</button>
                        </form>
                    </details>
                    <details style="display: inline-block; margin: 0 4px;">
                        <summary class="btn btn-secondary">Notes</summary>
                        <form method="post" style="margin-top: 8px;">
                            <?= Layout::csrfField() ?>
                            <input type="hidden" name="action" value="edit_notes">
                            <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                            <textarea name="notes" rows="10" style="font-family: 'Cascadia Code', Consolas, monospace; min-width: 400px;"><?= htmlspecialchars($existingNotes[$v['version']] ?? '') ?></textarea>
                            <br><br>
                            <button class="btn btn-primary" type="submit">Enregistrer les notes</button>
                        </form>
                    </details>
                    <form method="post" style="display:inline" onsubmit="return confirm('Retirer v<?= htmlspecialchars($v['version']) ?> du manifest ?\n(Le ZIP reste dans builds/, supprime-le en SFTP si voulu.)')">
                        <?= Layout::csrfField() ?>
                        <input type="hidden" name="action" value="delete">
                        <input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
                        <button class="btn btn-danger" type="submit">Retirer</button>
                    </form>
                </td>
            </tr>
        <?php endforeach; ?>
        <?php if (empty($manifest['versions'])): ?>
            <tr><td colspan="7" class="muted" style="text-align:center; padding: 32px;">Aucune version dans le manifest.</td></tr>
        <?php endif; ?>
        </tbody>
    </table>
</div>

<div class="card">
    <h2>ZIPs présents dans builds/</h2>
    <?php if (empty($zips)): ?>
        <p class="muted">Aucun ZIP. Upload tes fichiers via SFTP dans <code>www/PS_Launcher/builds/</code>, puis clique « Sync » ci-dessus.</p>
    <?php else: ?>
        <table>
            <thead><tr><th>Fichier</th><th>Taille</th><th>Référencé ?</th></tr></thead>
            <tbody>
            <?php
            $referencedNames = [];
            foreach ($manifest['versions'] ?? [] as $v) {
                $referencedNames[] = basename(parse_url($v['download']['url'], PHP_URL_PATH) ?? '');
            }
            foreach ($zips as $z):
                $referenced = in_array($z, $referencedNames, true);
            ?>
                <tr>
                    <td><code><?= htmlspecialchars($z) ?></code></td>
                    <td class="muted"><?= Layout::formatBytes($zipSizes[$z]) ?></td>
                    <td><?= $referenced
                        ? "<span class='badge badge-success'>oui</span>"
                        : "<span class='badge badge-warning'>orphelin</span>" ?></td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
    <?php endif; ?>
</div>
<?php Layout::footer();
