<?php
declare(strict_types=1);

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
    'httponly' => true,
    'samesite' => 'Lax'
]);
session_start();

function admin_normalize_code(string $code): string
{
    $code = strtoupper(trim($code));
    $code = preg_replace('/[^A-Z0-9\-]/', '', $code) ?? '';
    return trim($code, '-');
}

header('Content-Type: text/html; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

function admin_config_file(): string { return dirname(__DIR__) . '/privatedata/admin-config.php'; }
function admin_tokens_file(): string { return dirname(__DIR__) . '/privatedata/access-tokens.json'; }
function admin_devices_file(): string { return dirname(__DIR__) . '/privatedata/authorized-devices.json'; }
function admin_remember_file(): string { return dirname(__DIR__) . '/privatedata/remember-tokens.json'; }
function admin_activation_attempts_file(): string { return dirname(__DIR__) . '/privatedata/activation-attempts.json'; }

function admin_load_config(): array
{
    $file = admin_config_file();
    if (!is_file($file)) { return []; }
    $config = require $file;
    return is_array($config) ? $config : [];
}

function admin_check_credentials(string $login, string $password): bool
{
    $config = admin_load_config();
    $expectedLogin = trim((string) ($config['admin_login'] ?? ''));
    $expectedHash = trim((string) ($config['admin_password_hash'] ?? ''));
    $expectedPassword = (string) ($config['admin_password'] ?? '');

    $ok = $expectedLogin !== '' && hash_equals($expectedLogin, $login);
    if ($ok) {
        if ($expectedHash !== '') {
            $ok = password_verify($password, $expectedHash);
        } else {
            $ok = $expectedPassword !== '' && hash_equals($expectedPassword, $password);
        }
    }

    return $ok;
}

function admin_render_login(string $error = ''): void
{
    ?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Connexion admin Chrono Party</title>
<style>
:root{--bg:#0f1024;--card:#181a36;--line:rgba(255,255,255,.14);--txt:#fff;--muted:rgba(255,255,255,.68);--accent:#7c5cff;--bad:#ff8a8a}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0f1024,#21104c);color:var(--txt);font-family:Arial,Helvetica,sans-serif;padding:20px}
.card{width:100%;max-width:420px;background:rgba(255,255,255,.08);border:1px solid var(--line);border-radius:22px;padding:24px;box-shadow:0 18px 60px rgba(0,0,0,.3)}
h1{margin:0 0 8px;font-size:28px}.sub{margin:0 0 20px;color:var(--muted)}
label{display:block;font-size:13px;color:var(--muted);margin:14px 0 6px}
input{width:100%;padding:12px 14px;border-radius:14px;border:1px solid var(--line);background:rgba(255,255,255,.1);color:#fff;font-size:16px}
button{width:100%;margin-top:18px;border:0;border-radius:999px;padding:13px 16px;background:var(--accent);color:#fff;font-weight:800;cursor:pointer;font-size:16px}
.error{background:rgba(255,138,138,.14);border:1px solid rgba(255,138,138,.35);color:#fff;padding:11px 12px;border-radius:14px;margin:0 0 14px}




.rename-code-form{
  display:flex;
  gap:6px;
  align-items:center;
  flex-wrap:wrap;
}
.rename-code-form input{
  width:180px;
  max-width:100%;
  padding:7px 8px;
  border-radius:9px;
  border:1px solid rgba(255,255,255,.18);
}
.rename-code-form button{
  padding:7px 10px;
  border-radius:9px;
  border:0;
  background:#4f46e5;
  color:#fff;
  font-weight:700;
  cursor:pointer;
}


.rename-code-inline{
  display:flex;
  gap:6px;
  align-items:center;
  flex-wrap:wrap;
}
.rename-code-inline input{
  width:170px;
  max-width:100%;
  padding:8px 9px;
  border-radius:10px;
  border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.10);
  color:#fff;
  font-size:12px;
}
.rename-code-inline button{
  padding:8px 10px;
  border-radius:10px;
  border:0;
  background:#4f46e5;
  color:#fff;
  font-weight:700;
  cursor:pointer;
  font-size:12px;
}


.code-url-edit{
  margin-top:8px;
  display:flex;
  gap:6px;
  align-items:center;
  flex-wrap:wrap;
}
.code-url-edit input{
  width:170px;
  max-width:100%;
  padding:7px 8px;
  border-radius:9px;
  border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.10);
  color:#fff;
  font-size:12px;
}
.code-url-edit button{
  padding:7px 9px;
  border-radius:9px;
  border:0;
  background:#4f46e5;
  color:#fff;
  font-weight:700;
  cursor:pointer;
  font-size:12px;
}


.admin-sortable th.sorted-asc .admin-sort-arrow,
.admin-sortable th.sorted-desc .admin-sort-arrow{
  opacity:1;
}


</style>
</head>
<body>
  <form class="card" method="post">
    <h1>Admin Chrono Party</h1>
    <p class="sub">Connexion à l’espace de gestion des accès.</p>
    <?php if ($error !== ''): ?><div class="error"><?= h($error) ?></div><?php endif; ?>
    <input type="hidden" name="admin_login_action" value="1">
    <label>Identifiant admin</label>
    <input name="admin_login" autocomplete="username" required autofocus>
    <label>Mot de passe admin</label>
    <input name="admin_password" type="password" autocomplete="current-password" required>
    <button type="submit">Se connecter</button>
  </form>







</body>
</html>
    <?php
    exit;
}

function admin_require_session_login(): void
{
    if (!empty($_SESSION['chrono_admin_logged'])) {
        $_SESSION['chrono_admin_last_activity'] = time();
        return;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['admin_login_action'])) {
        $login = trim((string) ($_POST['admin_login'] ?? ''));
        $password = (string) ($_POST['admin_password'] ?? '');

        if (admin_check_credentials($login, $password)) {
            session_regenerate_id(true);
            $_SESSION['chrono_admin_logged'] = true;
            $_SESSION['chrono_admin_last_activity'] = time();
            $_SESSION['chrono_admin_login'] = $login;
            header('Location: ./');
            exit;
        }

        admin_render_login('Identifiant ou mot de passe incorrect.');
    }

    admin_render_login();
}

function admin_read_json(string $file): array
{
    if (!is_file($file)) { return []; }
    $data = json_decode((string) file_get_contents($file), true);
    return is_array($data) ? $data : [];
}

function admin_write_json(string $file, array $data): void
{
    file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), LOCK_EX);
}


function admin_clear_remember_tokens_for_gift(string $gift): void
{
    $file = admin_remember_file();
    $tokens = admin_read_json($file);
    if (!$tokens) { return; }

    foreach ($tokens as $selector => $entry) {
        if (is_array($entry) && strtoupper((string) ($entry['gift_code'] ?? '')) === $gift) {
            unset($tokens[$selector]);
        }
    }

    admin_write_json($file, $tokens);
}


function admin_update_remember_tokens_for_gift(string $oldGift, string $newGift): void
{
    $file = admin_remember_file();
    $remember = admin_read_json($file);
    if (!$remember) { return; }

    $oldKey = 'gift:' . $oldGift;
    $newKey = 'gift:' . $newGift;

    foreach ($remember as $selector => $entry) {
        if (!is_array($entry)) { continue; }

        if (($entry['gift_code'] ?? '') === $oldGift) {
            $remember[$selector]['gift_code'] = $newGift;
        }

        if (($entry['client_id'] ?? '') === $oldKey) {
            $remember[$selector]['client_id'] = $newKey;
        }
    }

    admin_write_json($file, $remember);
}


function admin_reset_requests_file(): string { return dirname(__DIR__) . '/privatedata/reset-requests.json'; }
function admin_load_reset_requests(): array
{
    $file = admin_reset_requests_file();
    if (!is_file($file)) { return []; }
    $decoded = json_decode((string) file_get_contents($file), true);
    return is_array($decoded) ? $decoded : [];
}
function admin_save_reset_requests(array $requests): void
{
    admin_write_json(admin_reset_requests_file(), array_values($requests));
}

function h(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); }
function today(): string { return date('Y-m-d'); }
function clean_code(string $value): string
{
    $value = strtoupper(trim($value));
    return preg_replace('/[^A-Z0-9_-]/', '', $value) ?? '';
}
function clean_date(string $value): string
{
    $value = trim($value);
    if ($value === '') { return ''; }
    return preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) ? $value : '';
}
function clean_access_type(string $value): string
{
    $value = strtolower(trim($value));
    $allowed = ['premium', 'gift', 'asso', 'test'];
    return in_array($value, $allowed, true) ? $value : 'premium';
}
function access_type_label(string $type): string
{
    return match ($type) {
        'gift' => 'Cadeau',
        'asso' => 'Association',
        'test' => 'Test',
        default => 'Premium',
    };
}
function find_token_index(array $tokens, string $gift): ?int
{
    foreach ($tokens as $i => $entry) {
        if (is_array($entry) && strtoupper((string) ($entry['gift'] ?? '')) === $gift) {
            return (int) $i;
        }
    }
    return null;
}
function device_count_for(array $devices, string $gift): int
{
    $key = 'gift:' . $gift;
    if (!isset($devices[$key]) || !is_array($devices[$key])) { return 0; }
    $list = $devices[$key]['devices'] ?? [];
    return is_array($list) ? count($list) : 0;
}
function status_label(array $entry): array
{
    $active = array_key_exists('active', $entry) ? (bool) $entry['active'] : true;
    $end = trim((string) ($entry['access_end'] ?? ''));
    if (!$active) { return ['Désactivé', 'bad']; }
    if ($end !== '' && today() > $end) { return ['Expiré', 'warn']; }
    return ['Actif', 'ok'];
}

admin_require_session_login();

if (empty($_SESSION['admin_csrf'])) {
    $_SESSION['admin_csrf'] = bin2hex(random_bytes(16));
}
$csrf = $_SESSION['admin_csrf'];
$message = '';
$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $postedCsrf = (string) ($_POST['csrf'] ?? '');
    if (!hash_equals($csrf, $postedCsrf)) {
        $error = 'Sécurité : formulaire invalide.';
    } else {
        $tokens = admin_read_json(admin_tokens_file());
        $devices = admin_read_json(admin_devices_file());
$resetRequests = admin_load_reset_requests();
        $action = (string) ($_POST['action'] ?? '');
        $gift = clean_code((string) ($_POST['gift'] ?? ''));
        $idx = $gift !== '' ? find_token_index($tokens, $gift) : null;

        if (false) {
        } elseif ($action === 'complete_reset_request') {
            $requestId = (string) ($_POST['request_id'] ?? '');
            $requestGift = clean_code((string) ($_POST['request_gift'] ?? ''));

            if ($requestGift !== '') {
                $deviceKey = 'gift:' . $requestGift;
                if (isset($devices[$deviceKey])) {
                    unset($devices[$deviceKey]);
                    admin_write_json(admin_devices_file(), $devices);
                }
                admin_clear_remember_tokens_for_gift($requestGift);
            }

            $requests = admin_load_reset_requests();
            foreach ($requests as $rIndex => $request) {
                if (is_array($request) && (string) ($request['id'] ?? '') === $requestId) {
                    unset($requests[$rIndex]);
                }
            }
            admin_save_reset_requests($requests);
            $message = 'Demande traitée et appareils réinitialisés.';
        } elseif ($action === 'delete_reset_request') {
            $requestId = (string) ($_POST['request_id'] ?? '');
            $requests = admin_load_reset_requests();
            foreach ($requests as $rIndex => $request) {
                if (is_array($request) && (string) ($request['id'] ?? '') === $requestId) {
                    unset($requests[$rIndex]);
                }
            }
            admin_save_reset_requests($requests);
            $message = 'Demande supprimée.';
} elseif ($action === 'delete_activation_attempt') {
    $attemptIndex = (int) ($_POST['attempt_index'] ?? -1);
    $attempts = admin_read_json(admin_activation_attempts_file());

    if (isset($attempts[$attemptIndex])) {
        unset($attempts[$attemptIndex]);
        admin_write_json(admin_activation_attempts_file(), array_values($attempts));

        header('Location: ./');
        exit;
    } else {
        $error = 'Tentative introuvable.';
    }
} elseif ($action === 'create') {
            $newGift = clean_code((string) ($_POST['new_gift'] ?? ''));
            $label = trim((string) ($_POST['label'] ?? ''));
            $accessType = clean_access_type((string) ($_POST['access_type'] ?? 'premium'));
            $accessEnd = clean_date((string) ($_POST['access_end'] ?? ''));
            $maxDevices = max(1, min(20, (int) ($_POST['max_devices'] ?? 2)));
            if ($newGift === '') {
                $error = 'Code obligatoire.';
            } elseif (find_token_index($tokens, $newGift) !== null) {
                $error = 'Ce code existe déjà.';
            } elseif ($accessEnd === '') {
                $error = 'Date d’expiration obligatoire au format AAAA-MM-JJ.';
            } else {
                $tokens[] = [
                    'gift' => $newGift,
                    'label' => $label !== '' ? $label : ('Code d’accès ' . $newGift),
                    'access_type' => $accessType,
                    'token' => '',
                    'devices' => [],
                    'activated' => false,
                    'active' => true,
                    'max_devices' => $maxDevices,
                    'devices_revision' => 0,
                    'access_end' => $accessEnd,
                    'created_at' => date('c')
                ];
                admin_write_json(admin_tokens_file(), $tokens);
                $message = 'Accès créé.';
            }
        } elseif ($action === 'rename_code') {
            $oldGift = clean_code((string) ($_POST['old_gift'] ?? ''));
            $newGift = clean_code((string) ($_POST['new_gift'] ?? ''));

            if ($oldGift === '' || $newGift === '') {
                $error = 'Code obligatoire.';
            } elseif ($oldGift === $newGift) {
                $message = 'Code inchangé.';
            } elseif (find_token_index($tokens, $newGift) !== null) {
                $error = 'Ce nouveau code existe déjà.';
            } else {
                $oldIndex = find_token_index($tokens, $oldGift);

                if ($oldIndex === null) {
                    $error = 'Accès introuvable.';
                } else {
                    $tokens[$oldIndex]['gift'] = $newGift;
                    $tokens[$oldIndex]['updated_at'] = date('c');
                    admin_write_json(admin_tokens_file(), $tokens);

                    $oldDeviceKey = 'gift:' . $oldGift;
                    $newDeviceKey = 'gift:' . $newGift;

                    if (isset($devices[$oldDeviceKey])) {
                        $devices[$newDeviceKey] = $devices[$oldDeviceKey];
                        unset($devices[$oldDeviceKey]);
                        admin_write_json(admin_devices_file(), $devices);
                    }

                    admin_update_remember_tokens_for_gift($oldGift, $newGift);

                    $message = 'Code URL modifié.';
                }
            }
        } elseif ($idx === null) {
            $error = 'Accès introuvable.';
        } elseif ($action === 'save') {
            $label = trim((string) ($_POST['label'] ?? ''));
            $accessType = clean_access_type((string) ($_POST['access_type'] ?? 'premium'));
            $accessEnd = clean_date((string) ($_POST['access_end'] ?? ''));
            $maxDevices = max(1, min(20, (int) ($_POST['max_devices'] ?? 2)));
            if ($accessEnd === '') {
                $error = 'Date d’expiration obligatoire au format AAAA-MM-JJ.';
            } else {
                $tokens[$idx]['label'] = $label !== '' ? $label : ('Code d’accès ' . $gift);
                $tokens[$idx]['access_type'] = $accessType;
                $tokens[$idx]['access_end'] = $accessEnd;
                $tokens[$idx]['max_devices'] = $maxDevices;
                $tokens[$idx]['active'] = !empty($_POST['active']);
                $tokens[$idx]['updated_at'] = date('c');
                admin_write_json(admin_tokens_file(), $tokens);

                $deviceKey = 'gift:' . $gift;
                if (isset($devices[$deviceKey]) && is_array($devices[$deviceKey])) {
                    $devices[$deviceKey]['label'] = $tokens[$idx]['label'];
                    $devices[$deviceKey]['max_devices'] = $maxDevices;
                    admin_write_json(admin_devices_file(), $devices);
                }
                $message = 'Accès mis à jour.';
            }
        } elseif ($action === 'toggle') {
            $tokens[$idx]['active'] = !(array_key_exists('active', $tokens[$idx]) ? (bool) $tokens[$idx]['active'] : true);
            $tokens[$idx]['updated_at'] = date('c');
            admin_write_json(admin_tokens_file(), $tokens);
            $message = 'Statut modifié.';
} elseif ($action === 'reset_devices') {
    unset($devices['gift:' . $gift]);
    unset($devices[$gift]);

    $tokens[$idx]['devices_revision'] = (int) ($tokens[$idx]['devices_revision'] ?? 0) + 1;
    $tokens[$idx]['devices_reset_at'] = date('c');

    admin_write_json(admin_devices_file(), $devices);
    admin_write_json(admin_tokens_file(), $tokens);
    admin_clear_remember_tokens_for_gift($gift);
$attempts = admin_read_json(admin_activation_attempts_file());

$attempts = array_values(array_filter(
    $attempts,
    function($attempt) use ($gift){
        if (!is_array($attempt)) {
            return false;
        }

        $code = strtoupper((string)($attempt['code'] ?? ''));
        $accessKey = strtoupper((string)($attempt['access_key'] ?? ''));

        return $code !== $gift
            && $accessKey !== $gift
            && $accessKey !== ('GIFT:' . $gift);
    }
));

admin_write_json(admin_activation_attempts_file(), $attempts);
    header('Location: ./');
    exit;
} elseif ($action === 'delete') {
            array_splice($tokens, $idx, 1);
            unset($devices['gift:' . $gift]);
            admin_write_json(admin_tokens_file(), $tokens);
            admin_write_json(admin_devices_file(), $devices);
            $message = 'Code supprimé.';
        }
    }
}

$tokens = admin_read_json(admin_tokens_file());
$devices = admin_read_json(admin_devices_file());
usort($tokens, function ($a, $b) {
    return strcmp((string) ($a['gift'] ?? ''), (string) ($b['gift'] ?? ''));
});
$scheme = (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') ? 'https' : 'http';
$host = (string) ($_SERVER['HTTP_HOST'] ?? '');
$scriptDir = rtrim(str_replace('\\', '/', dirname((string) ($_SERVER['SCRIPT_NAME'] ?? '/admin/index.php'))), '/');
$siteRoot = preg_replace('#/admin$#', '', $scriptDir) ?: '';
$resetRequests = admin_load_reset_requests();

$activationAttempts = admin_read_json(admin_activation_attempts_file());
$activationAttempts = array_reverse($activationAttempts, true);
$securityAlertsFile = dirname(__DIR__) . '/privatedata/security-alerts.log';
$securityAlertsCount = 0;

if (is_file($securityAlertsFile)) {
    $securityAlertsLines = file($securityAlertsFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    $securityAlertsCount = is_array($securityAlertsLines) ? count($securityAlertsLines) : 0;
}
$base = $host !== '' ? ($scheme . '://' . $host . $siteRoot . '/activation/?gift=') : ('../activation/?gift=');
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Admin accès Chrono Party</title>
<style>
:root{--bg:#0f1024;--card:#181a36;--line:rgba(255,255,255,.12);--txt:#fff;--muted:rgba(255,255,255,.68);--accent:#7c5cff;--ok:#79e39b;--warn:#ffd166;--bad:#ff8a8a}*{box-sizing:border-box}body{margin:0;background:linear-gradient(135deg,#0f1024,#21104c);color:var(--txt);font-family:Arial,Helvetica,sans-serif;padding:22px}.wrap{max-width:1200px;margin:auto}h1{margin:0 0 8px;font-size:30px}.sub{margin:0 0 22px;color:var(--muted)}.card{background:rgba(255,255,255,.08);border:1px solid var(--line);border-radius:20px;padding:18px;margin:0 0 18px;box-shadow:0 16px 50px rgba(0,0,0,.25)}.grid{display:grid;grid-template-columns:1.2fr 1.5fr 150px 160px 120px auto;gap:10px;align-items:end}.field label{display:block;font-size:12px;color:var(--muted);margin:0 0 5px}input,select{width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--line);background:rgba(255,255,255,.08);color:#fff}select option{color:#111;background:#fff}button,.btn{border:0;border-radius:999px;padding:10px 13px;background:var(--accent);color:#fff;font-weight:700;cursor:pointer;text-decoration:none;display:inline-block}.btn2{background:rgba(255,255,255,.13)}.danger{background:#9d2d4f}.mini{font-size:12px;padding:8px 10px}.msg{padding:12px 14px;border-radius:14px;margin:0 0 15px}.success{background:rgba(121,227,155,.15);border:1px solid rgba(121,227,155,.35)}.error{background:rgba(255,138,138,.14);border:1px solid rgba(255,138,138,.35)}table{width:100%;border-collapse:collapse;overflow:hidden}th,td{padding:12px 10px;border-bottom:1px solid var(--line);vertical-align:middle;text-align:left}th{font-size:12px;text-transform:uppercase;color:var(--muted);letter-spacing:.04em}.pill{padding:6px 9px;border-radius:999px;font-weight:800;font-size:12px;display:inline-block}.ok{background:rgba(121,227,155,.16);color:var(--ok)}.warn{background:rgba(255,209,102,.16);color:var(--warn)}.bad{background:rgba(255,138,138,.16);color:var(--bad)}.actions{display:flex;gap:6px;flex-wrap:wrap}.copy{font-family:monospace;font-size:12px;color:var(--muted);word-break:break-all}.inline{display:inline}.check{width:auto}.empty{color:var(--muted);padding:25px;text-align:center}@media(max-width:900px){.grid{grid-template-columns:1fr 1fr}.tablewrap{overflow:auto}table{min-width:1000px}}@media(max-width:560px){.grid{grid-template-columns:1fr}}

.admin-logout-bar{
  display:flex;
  justify-content:flex-end;
  margin:0 0 18px;
}
.admin-logout-button{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding:10px 14px;
  border-radius:12px;
  background:#ef4444;
  color:#ffffff !important;
  font-weight:700;
  text-decoration:none;
  box-shadow:0 8px 18px rgba(239,68,68,.22);
}
.admin-logout-button:hover{
  filter:brightness(.95);
}


</style>
</head>
<body>

<div class="admin-logout-bar">
  <a class="admin-logout-button" href="logout.php">Déconnexion</a>
</div>

<div class="wrap">
  <h1>Admin accès Chrono Party</h1>
  <p class="sub">Gestion des codes, dates d’expiration, activation/désactivation et limite d’appareils.</p>
  <div class="card">
  <h2 style="margin-top:0">🔐 Sécurité</h2>
  <p class="sub">
    Alertes API suspectes :
    <strong><?= (int)$securityAlertsCount ?></strong>
  </p>
  <a class="btn" href="/jeux/admin/security/">Voir les alertes sécurité</a>
</div>

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

  <div class="card">
    <h2 style="margin-top:0">Créer un code</h2>
    <form method="post" class="grid">
      <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
      <input type="hidden" name="action" value="create">
      <div class="field"><label>Code</label><input name="new_gift" placeholder="CP-MARC-4829" required></div>
      <div class="field"><label>Nom / client</label><input name="label" placeholder="Marc Dupont"></div>
      <div class="field"><label>Type</label><select name="access_type"><option value="premium">Premium</option><option value="gift">Cadeau</option><option value="asso">Association</option><option value="test">Test</option></select></div>
      <div class="field"><label>Expire le</label><input type="date" name="access_end" value="<?= h(date('Y-m-d', strtotime('+1 year'))) ?>" required></div>
      <div class="field"><label>Appareils</label><input type="number" name="max_devices" value="2" min="1" max="20"></div>
      <button type="submit">Créer</button>
    </form>
  </div>


  <div class="card tablewrap">
    <h2 style="margin-top:0">Demandes de réinitialisation</h2>
    <?php if (!$resetRequests): ?>
      <div class="empty">Aucune demande en attente.</div>
    <?php else: ?>
      <table>
        <thead><tr><th>Date</th><th>Code URL</th><th>Nom</th><th>Email</th><th>Message</th><th>Actions</th></tr></thead>
        <tbody>
        <?php foreach (array_reverse($resetRequests) as $request):
          if (!is_array($request)) { continue; }
          $requestId = (string) ($request['id'] ?? '');
          $requestGift = clean_code((string) ($request['gift'] ?? ''));
        ?>
          <tr>
            <td><span class="copy"><?= h((string) ($request['created_at'] ?? '')) ?></span></td>
            <td><strong><?= h($requestGift) ?></strong></td>
            <td><?= h((string) ($request['name'] ?? '')) ?></td>
            <td><a href="mailto:<?= h((string) ($request['email'] ?? '')) ?>"><?= h((string) ($request['email'] ?? '')) ?></a></td>
            <td><?= nl2br(h((string) ($request['message'] ?? ''))) ?></td>
            <td>
              <div class="actions">
                <form class="inline" method="post" onsubmit="return confirm('Réinitialiser les appareils de cet accès ?');">
                  <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                  <input type="hidden" name="action" value="complete_reset_request">
                  <input type="hidden" name="request_id" value="<?= h($requestId) ?>">
                  <input type="hidden" name="request_gift" value="<?= h($requestGift) ?>">
                  <button class="mini" type="submit">Reset appareils</button>
                </form>
                <form class="inline" method="post" onsubmit="return confirm('Supprimer cette demande ?');">
                  <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                  <input type="hidden" name="action" value="delete_reset_request">
                  <input type="hidden" name="request_id" value="<?= h($requestId) ?>">
                  <button class="mini danger" type="submit">Supprimer</button>
                </form>
              </div>
            </td>
          </tr>
        <?php endforeach; ?>
        </tbody>
      </table>
    <?php endif; ?>
  </div>


  <div class="card tablewrap">
    <h2 style="margin-top:0">Tentatives refusées</h2>
    <p class="sub">Affiche les essais bloqués quand un code dépasse sa limite d’appareils.</p>
    <?php if (empty($activationAttempts)): ?>
      <div class="empty">Aucune tentative refusée enregistrée.</div>
    <?php else: ?>
      <table>
        <thead><tr><th>Date</th><th>Code</th><th>Nom</th><th>Raison</th><th>Appareils</th><th>IP</th><th>Navigateur</th><th>Actions</th></tr></thead>
        <tbody>
        <?php foreach ($activationAttempts as $attemptIndex => $attempt):
          if (!is_array($attempt)) { continue; }
          $date = (string) ($attempt['created_at'] ?? '');
          $code = (string) ($attempt['code'] ?? $attempt['access_key'] ?? '');
          $label = (string) ($attempt['label'] ?? '');
          $reason = (string) ($attempt['reason'] ?? '');
          $used = (int) ($attempt['used'] ?? 0);
          $max = (int) ($attempt['max'] ?? 0);
          $ip = (string) ($attempt['ip'] ?? '');
          $browser = (string) ($attempt['browser'] ?? '');
        ?>
          <tr>
            <td><?= h($date) ?></td>
            <td><strong><?= h($code) ?></strong></td>
            <td><?= h($label) ?></td>
            <td><span class="pill bad"><?= h($reason === 'device_limit' ? 'Limite appareils' : $reason) ?></span></td>
            <td><?= h((string) $used) ?> / <?= h((string) $max) ?></td>
            <td><?= h($ip) ?></td>
            <td><?= h($browser) ?></td>
            <td>
              <form class="inline" method="post" onsubmit="return confirm('Supprimer cette tentative refusée ?');">
                <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                <input type="hidden" name="action" value="delete_activation_attempt">
                <input type="hidden" name="attempt_index" value="<?= h((string) $attemptIndex) ?>">
                <button class="mini danger" type="submit">Supprimer</button>
              </form>
            </td>
          </tr>
        <?php endforeach; ?>
        </tbody>
      </table>
    <?php endif; ?>
  </div>

  <div class="card tablewrap">
    <h2 style="margin-top:0">Accès existants</h2>
    <?php if (!$tokens): ?>
      <div class="empty">Aucun code trouvé.</div>
    <?php else: ?>
<table>
        <thead><tr><th>Code URL</th><th>Nom</th><th>Type</th><th>Statut</th><th>Expire</th><th>Appareils</th><th>Lien</th><th>Actions</th></tr></thead>
        <tbody>
        <?php foreach ($tokens as $entry):
          if (!is_array($entry)) { continue; }
          $gift = clean_code((string) ($entry['gift'] ?? ''));
          if ($gift === '') { continue; }
          [$labelStatus, $classStatus] = status_label($entry);
          $maxDevices = max(1, (int) ($entry['max_devices'] ?? 2));
          $usedDevices = device_count_for($devices, $gift);
          $link = $base . rawurlencode($gift);
        ?>
          <tr>
            <td>
              <strong><?= h($gift) ?></strong>
              <form method="post" class="code-url-edit" onsubmit="return confirm('Modifier ce code URL ? L’ancien lien ne fonctionnera plus.');">
                <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                <input type="hidden" name="action" value="rename_code">
                <input type="hidden" name="old_gift" value="<?= h($gift) ?>">
                <input type="hidden" name="gift" value="<?= h($gift) ?>">
                <input type="text" name="new_gift" value="<?= h($gift) ?>" pattern="[A-Z0-9\-]+" title="Lettres majuscules, chiffres et tirets uniquement">
                <button type="submit">Modifier URL</button>
              </form>
              <span class="copy"><?= h((string) ($entry['token'] ?? '')) ?></span>
            </td>
            <td>
              <form method="post" id="save-<?= h($gift) ?>">
                <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                <input type="hidden" name="action" value="save">
                <input type="hidden" name="gift" value="<?= h($gift) ?>">
              </form>
              <input form="save-<?= h($gift) ?>" name="label" value="<?= h((string) ($entry['label'] ?? ('Code d’accès ' . $gift))) ?>">
            </td>
            <td>
              <?php $accessType = clean_access_type((string) ($entry['access_type'] ?? 'premium')); ?>
              <select form="save-<?= h($gift) ?>" name="access_type">
                <option value="premium" <?= $accessType === 'premium' ? 'selected' : '' ?>>Premium</option>
                <option value="gift" <?= $accessType === 'gift' ? 'selected' : '' ?>>Cadeau</option>
                <option value="asso" <?= $accessType === 'asso' ? 'selected' : '' ?>>Association</option>
                <option value="test" <?= $accessType === 'test' ? 'selected' : '' ?>>Test</option>
              </select>
            </td>
            <td><span class="pill <?= h($classStatus) ?>"><?= h($labelStatus) ?></span><br><label style="font-size:12px;color:var(--muted)"><input form="save-<?= h($gift) ?>" class="check" type="checkbox" name="active" value="1" <?= (array_key_exists('active', $entry) ? (bool) $entry['active'] : true) ? 'checked' : '' ?>> actif</label></td>
            <td><input form="save-<?= h($gift) ?>" type="date" name="access_end" value="<?= h((string) ($entry['access_end'] ?? '')) ?>"></td>
            <td><input form="save-<?= h($gift) ?>" type="number" name="max_devices" value="<?= h((string) $maxDevices) ?>" min="1" max="20"><br><span class="copy"><?= $usedDevices ?> / <?= $maxDevices ?></span></td>
            <td><span class="copy"><?= h($link) ?></span></td>
            <td>
                <div class="actions">
                  <button form="save-<?= h($gift) ?>" class="mini" type="submit">Sauver</button>
              <form class="inline" method="post"><input type="hidden" name="csrf" value="<?= h($csrf) ?>"><input type="hidden" name="gift" value="<?= h($gift) ?>"><input type="hidden" name="action" value="toggle"><button class="mini btn2" type="submit"><?= (array_key_exists('active', $entry) ? (bool) $entry['active'] : true) ? 'Désactiver' : 'Réactiver' ?></button></form>
              <form class="inline" method="post" onsubmit="return confirm('Remettre les appareils de ce code à zéro ?');"><input type="hidden" name="csrf" value="<?= h($csrf) ?>"><input type="hidden" name="gift" value="<?= h($gift) ?>"><input type="hidden" name="action" value="reset_devices"><button class="mini btn2" type="submit">Reset appareils</button></form>
              <form class="inline" method="post" onsubmit="return confirm('Supprimer définitivement cet accès ?');"><input type="hidden" name="csrf" value="<?= h($csrf) ?>"><input type="hidden" name="gift" value="<?= h($gift) ?>"><input type="hidden" name="action" value="delete"><button class="mini danger" type="submit">Supprimer</button></form>
                </div>
            </td>
          </tr>
        <?php endforeach; ?>
        </tbody>
      </table>
    <?php endif; ?>
  </div>
</div>




<script>
(function(){
  function initAdminSort(){
    const table = document.querySelector(".tablewrap table");
    if(!table) return;

    table.classList.add("admin-sortable");

    const headerRow = table.querySelector("thead tr");
    const tbody = table.querySelector("tbody");
    if(!headerRow || !tbody) return;

    const headers = Array.from(headerRow.querySelectorAll("th"));
    let currentIndex = -1;
    let currentDirection = "asc";

    function normalize(text){
      return (text || "").toString().toLowerCase()
        .normalize("NFD").replace(/[\u0300-\u036f]/g, "").trim();
    }

    function cleanHeaderText(header){
      const clone = header.cloneNode(true);
      clone.querySelectorAll(".admin-sort-arrow").forEach(el => el.remove());
      return normalize(clone.textContent);
    }

    function getSortValue(row, index){
      const cell = row.children[index];
      if(!cell) return "";

      const h = cleanHeaderText(headers[index]);

      if(h.includes("code")){
        const input = cell.querySelector('input[name="new_gift"]');
        if(input) return input.value || "";
        const strong = cell.querySelector("strong");
        if(strong) return strong.textContent || "";
      }

      if(h.includes("nom")){
        const input = cell.querySelector('input[name="label"]');
        if(input) return input.value || "";
      }

      if(h.includes("statut")){
        const pill = cell.querySelector(".pill");
        if(pill) return pill.textContent || "";
        const checkbox = cell.querySelector('input[type="checkbox"]');
        if(checkbox) return checkbox.checked ? "actif" : "inactif";
      }

      if(h.includes("expire")){
        const input = cell.querySelector('input[name="access_end"]');
        if(input) return input.value || "";
      }

      if(h.includes("appareil")){
        const copy = cell.querySelector(".copy");
        if(copy){
          const match = copy.textContent.match(/(\d+)\s*\/\s*(\d+)/);
          if(match){
            return String(Number(match[1])).padStart(6, "0") + "/" + String(Number(match[2])).padStart(6, "0");
          }
        }
        const input = cell.querySelector('input[name="max_devices"]');
        if(input) return input.value || "";
      }

      if(h.includes("lien")){
        const copy = cell.querySelector(".copy");
        if(copy) return copy.textContent || "";
      }

      const input = cell.querySelector("input, select, textarea");
      if(input){
        if(input.type === "checkbox") return input.checked ? "1" : "0";
        return input.value || "";
      }

      return cell.textContent.trim();
    }

    function toDate(value){
      value = String(value || "").trim();
      if(/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
      if(/^\d{2}\/\d{2}\/\d{4}$/.test(value)){
        const p = value.split("/");
        return p[2] + "-" + p[1] + "-" + p[0];
      }
      return "";
    }

    function compareValues(a, b){
      const da = toDate(a);
      const db = toDate(b);
      if(da && db) return da.localeCompare(db);

      const na = Number(String(a).replace(",", "."));
      const nb = Number(String(b).replace(",", "."));
      if(String(a).trim() !== "" && String(b).trim() !== "" && !Number.isNaN(na) && !Number.isNaN(nb)){
        return na - nb;
      }

      return normalize(a).localeCompare(normalize(b), "fr", {numeric:true});
    }

    headers.forEach(function(header, index){
      header.querySelectorAll(".admin-sort-arrow").forEach(el => el.remove());

      const arrow = document.createElement("span");
      arrow.className = "admin-sort-arrow";
      arrow.textContent = "↕";
      header.appendChild(arrow);

      header.addEventListener("click", function(event){
        event.preventDefault();

        if(currentIndex === index){
          currentDirection = currentDirection === "asc" ? "desc" : "asc";
        }else{
          currentIndex = index;
          currentDirection = "asc";
        }

        headers.forEach(function(h){
          h.classList.remove("sorted");
          const a = h.querySelector(".admin-sort-arrow");
          if(a) a.textContent = "↕";
        });

        header.classList.add("sorted");
        arrow.textContent = currentDirection === "asc" ? "▲" : "▼";

        const rows = Array.from(tbody.querySelectorAll("tr"));
        rows.sort(function(rowA, rowB){
          const va = getSortValue(rowA, index);
          const vb = getSortValue(rowB, index);
          const result = compareValues(va, vb);
          return currentDirection === "asc" ? result : -result;
        });

        rows.forEach(row => tbody.appendChild(row));
      });
    });
  }

  if(document.readyState === "loading"){
    document.addEventListener("DOMContentLoaded", initAdminSort);
  }else{
    initAdminSort();
  }
})();
</script>

</body>
</html>
