<?php

declare(strict_types=1);

$root = __DIR__;
$message = '';
$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $token = (string) ($_POST['token'] ?? '');
    if (!adminTokenIsValid($token)) {
        $error = 'Unauthorized.';
    } else {
        $version = normalizeAdminVersion((string) ($_POST['version'] ?? ''));
        $action = (string) ($_POST['action'] ?? '');
        try {
            if ($action === 'package') {
                $message = 'Generated ' . basename(adminGeneratePackage($root, $version)) . '.';
            } elseif ($action === 'latest') {
                adminMirrorRelease($root, $version, true);
                $package = adminGeneratePackage($root, $version);
                adminSetLatest($root, $version, $package);
                $message = 'Set ' . $version . ' as latest.';
            } elseif ($action === 'publish') {
                adminMirrorRelease($root, $version, false);
                $package = adminGeneratePackage($root, $version);
                adminRegisterRelease($root, $version, $package);
                $message = 'Published ' . $version . '.';
            }
        } catch (Throwable $exception) {
            $error = $exception->getMessage();
        }
    }
}

$releases = adminReadJson($root . '/versions/releases.json', []);
$latest = adminReadJson($root . '/versions/latest.json', []);

?><!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BlockWriteAI Releases</title>
  <style>
    body{margin:0;background:#f5f7fb;color:#111827;font:15px/1.5 Arial,sans-serif}
    main{max-width:1080px;margin:0 auto;padding:34px 18px}
    header{display:flex;justify-content:space-between;gap:16px;align-items:end;margin-bottom:22px}
    h1{margin:0;font-size:28px}
    table{width:100%;border-collapse:collapse;background:#fff;border:1px solid #d8e0ec}
    th,td{padding:12px;border-bottom:1px solid #e5ebf3;text-align:left;vertical-align:middle}
    th{font-size:12px;text-transform:uppercase;color:#526178;background:#eef3f9}
    input,button{font:inherit;border:1px solid #bdc8d8;border-radius:6px;padding:8px 10px;background:#fff}
    button{cursor:pointer;font-weight:700}
    .actions{display:flex;gap:8px;flex-wrap:wrap}
    .token{display:flex;gap:8px;align-items:center}
    .notice{padding:10px 12px;margin-bottom:14px;border-radius:6px;border:1px solid #b9dec6;background:#eefbf2}
    .error{border-color:#f0b7b7;background:#fff1f1}
    .status{font-weight:700;color:#0f766e}
    a{color:#174ea6;font-weight:700}
  </style>
</head>
<body>
<main>
  <header>
    <div>
      <h1>BlockWriteAI Releases</h1>
      <div>Latest: <?= htmlspecialchars((string) ($latest['version'] ?? '')) ?></div>
    </div>
    <form class="token" method="post">
      <input type="password" name="token" placeholder="Admin token" autocomplete="off">
    </form>
  </header>

  <?php if ($message !== ''): ?><div class="notice"><?= htmlspecialchars($message) ?></div><?php endif; ?>
  <?php if ($error !== ''): ?><div class="notice error"><?= htmlspecialchars($error) ?></div><?php endif; ?>

  <table>
    <thead>
      <tr>
        <th>Version</th>
        <th>Release Date</th>
        <th>Status</th>
        <th>Download ZIP</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      <?php foreach ($releases as $release): ?>
        <?php
          $version = (string) ($release['version'] ?? '');
          $zip = 'packages/blockwriteai-' . $version . '.zip';
          $status = $version !== '' && $version === ($latest['version'] ?? '') ? 'latest' : (string) ($release['status'] ?? 'published');
        ?>
        <tr>
          <td><?= htmlspecialchars($version) ?></td>
          <td><?= htmlspecialchars((string) ($release['release_date'] ?? '')) ?></td>
          <td class="status"><?= htmlspecialchars($status) ?></td>
          <td><?php if (is_file($root . '/' . $zip)): ?><a href="<?= htmlspecialchars($zip) ?>">Download ZIP</a><?php else: ?>Not generated<?php endif; ?></td>
          <td>
            <div class="actions">
              <form method="post"><input type="hidden" name="version" value="<?= htmlspecialchars($version) ?>"><input type="password" name="token" placeholder="Token"><button name="action" value="package">Download ZIP</button></form>
              <form method="post"><input type="hidden" name="version" value="<?= htmlspecialchars($version) ?>"><input type="password" name="token" placeholder="Token"><button name="action" value="publish">Publish Release</button></form>
              <form method="post"><input type="hidden" name="version" value="<?= htmlspecialchars($version) ?>"><input type="password" name="token" placeholder="Token"><button name="action" value="latest">Set Latest</button></form>
            </div>
          </td>
        </tr>
      <?php endforeach; ?>
    </tbody>
  </table>
</main>
</body>
</html>
<?php

function adminTokenIsValid(string $token): bool
{
    $expected = getenv('DISTRIBUTION_API_TOKEN') ?: '';
    $local = __DIR__ . '/api/config.local.php';
    if ($expected === '' && is_file($local)) {
        $config = require $local;
        $expected = is_array($config) ? (string) ($config['api_token'] ?? '') : '';
    }

    return $expected !== '' && hash_equals($expected, $token);
}

function normalizeAdminVersion(string $version): string
{
    if (!preg_match('/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/', $version)) {
        throw new RuntimeException('A valid semantic version is required.');
    }
    return $version;
}

function adminPublicUrl(string $path): string
{
    $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    $host = (string) ($_SERVER['HTTP_HOST'] ?? 'localhost');
    $base = rtrim(str_replace('\\', '/', dirname((string) ($_SERVER['SCRIPT_NAME'] ?? '/distribution-server/admin.php'))), '/');
    return $scheme . '://' . $host . $base . '/' . ltrim($path, '/');
}

function adminReadJson(string $path, array $default): array
{
    if (!is_file($path)) {
        return $default;
    }
    $decoded = json_decode((string) file_get_contents($path), true);
    return is_array($decoded) ? $decoded : $default;
}

function adminWriteJson(string $path, array $data): void
{
    file_put_contents($path, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL, LOCK_EX);
}

function adminMirrorRelease(string $root, string $version, bool $latest): void
{
    adminMirrorDirectory($root . '/releases/' . $version, $root . '/cdn/' . $version);
    if ($latest) {
        adminMirrorDirectory($root . '/releases/' . $version, $root . '/cdn/latest');
        adminMirrorDirectory($root . '/releases/' . $version . '/vendor', $root . '/cdn/vendor');
    }
}

function adminMirrorDirectory(string $source, string $target): void
{
    if (!is_dir($source)) {
        throw new RuntimeException('Release folder is missing.');
    }
    if (!is_dir($target)) {
        mkdir($target, 0775, true);
    }
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
    foreach ($iterator as $item) {
        $destination = $target . '/' . substr($item->getPathname(), strlen($source) + 1);
        if ($item->isDir()) {
            if (!is_dir($destination)) {
                mkdir($destination, 0775, true);
            }
        } elseif (!str_ends_with($item->getFilename(), '.map')) {
            if (!is_dir(dirname($destination))) {
                mkdir(dirname($destination), 0775, true);
            }
            copy($item->getPathname(), $destination);
        }
    }
}

function adminRegisterRelease(string $root, string $version, string $package): void
{
    $releases = adminReadJson($root . '/versions/releases.json', []);
    $releases = array_values(array_filter($releases, static fn (array $release): bool => ($release['version'] ?? '') !== $version));
    $releases[] = [
        'version' => $version,
        'release_date' => gmdate('Y-m-d'),
        'cdn_base' => adminPublicUrl('cdn/' . $version),
        'download_url' => adminPublicUrl('packages/' . basename($package)),
        'package' => basename($package),
        'status' => 'published',
        'changelog' => [],
        'published_at' => gmdate('c'),
    ];
    usort($releases, static fn (array $a, array $b): int => version_compare((string) $b['version'], (string) $a['version']));
    adminWriteJson($root . '/versions/releases.json', $releases);
}

function adminSetLatest(string $root, string $version, string $package): void
{
    $releases = adminReadJson($root . '/versions/releases.json', []);
    foreach ($releases as &$release) {
        if (($release['version'] ?? '') === $version) {
            $release['status'] = 'latest';
            $release['cdn_base'] = adminPublicUrl('cdn/' . $version);
            $release['download_url'] = adminPublicUrl('packages/' . basename($package));
            adminWriteJson($root . '/versions/latest.json', array_merge($release, ['cdn_base' => adminPublicUrl('cdn/latest')]));
        } elseif (($release['status'] ?? '') === 'latest') {
            $release['status'] = 'published';
        }
    }
    unset($release);
    adminWriteJson($root . '/versions/releases.json', $releases);
}

function adminGeneratePackage(string $root, string $version): string
{
    $command = PHP_BINARY . ' ' . escapeshellarg($root . '/tools/generate_release.php') . ' ' . escapeshellarg($version);
    $output = [];
    $code = 0;
    exec($command, $output, $code);
    if ($code !== 0) {
        throw new RuntimeException('Release ZIP generation failed.');
    }
    return $root . '/packages/blockwriteai-' . $version . '.zip';
}
