<?php

declare(strict_types=1);

/**
 * Sysop Console guard + chrome (SPEC §11). Every admin page starts with
 * require '_lib.php' then admin_require() (§19.11). Auth rides a separate
 * PHP session; CSRF is verified centrally for every POST; all output goes
 * through h(), no exceptions.
 */

// Old-PHP guard (kept PHP 7-parseable): route to the installer's explanation.
if (PHP_VERSION_ID < 80100) {
    header('Location: ../install.php');
    exit;
}

require_once __DIR__ . '/../lib/bootstrap.php';

if (!atdt_installed()) {
    header('Location: ../install.php');
    exit;
}
atdt_migrate();

atdt_admin_session_start();

/** The one escape hatch to HTML. Everything printed goes through here. */
function h(mixed $s): string
{
    return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8');
}

/** Logged-in level-255 user row, or null. Demotion kills the session. */
function admin_user(): ?array
{
    static $user = false;
    if ($user !== false) {
        return $user;
    }
    $uid = atdt_admin_uid();
    $user = $uid > 0 ? atdt_db()->one('SELECT * FROM users WHERE id = ?', [$uid]) : null;
    if ($user !== null && ((int)$user['level'] !== 255 || $user['status'] !== 'active')) {
        unset($_SESSION['admin_uid']);
        $user = null;
    }
    return $user;
}

/** Gate at the top of every page: login wall + central CSRF check (§19.4). */
function admin_require(): array
{
    $user = admin_user();
    if ($user === null) {
        header('Location: login.php');
        exit;
    }
    if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
        admin_verify_csrf();
    }
    return $user;
}

function admin_csrf_token(): string
{
    if (!isset($_SESSION['csrf']) || !is_string($_SESSION['csrf'])) {
        $_SESSION['csrf'] = bin2hex(random_bytes(16));
    }
    return $_SESSION['csrf'];
}

function csrf_field(): string
{
    return '<input type="hidden" name="csrf" value="' . h(admin_csrf_token()) . '">';
}

function admin_verify_csrf(): void
{
    $sent = (string)($_POST['csrf'] ?? '');
    if ($sent === '' || !hash_equals(admin_csrf_token(), $sent)) {
        atdt_log_event('security', 'admin:' . client_ip(), 'CSRF check failed on ' . basename((string)($_SERVER['SCRIPT_NAME'] ?? '?')));
        http_response_code(403);
        echo 'Cross-request token mismatch. Go back, reload the page, and retry.';
        exit;
    }
}

function client_ip(): string
{
    return substr((string)($_SERVER['REMOTE_ADDR'] ?? 'local'), 0, 45);
}

/** A stored UTC stamp rendered in board time (the Settings timezone). */
function admin_stamp(string $utc, string $format = 'Y-m-d H:i'): string
{
    return atdt_board_stamp($utc, $format, atdt_settings());
}

/** Flash messages survive the POST→redirect→GET hop. */
function flash_set(string $kind, string $msg): void
{
    $_SESSION['flash'] = [$kind, $msg];
}

/** @return ?array{0: string, 1: string} */
function flash_take(): ?array
{
    $f = $_SESSION['flash'] ?? null;
    unset($_SESSION['flash']);
    return is_array($f) ? $f : null;
}

function admin_redirect(string $to): never
{
    header('Location: ' . $to);
    exit;
}

/** Uniform audit line for console actions (§8.2 events). */
function admin_log(string $msg): void
{
    $u = admin_user();
    atdt_log_event('admin', 'console:' . ($u === null ? '?' : $u['handle']), $msg);
}

// ---------------------------------------------------------------------------
// Chrome

function admin_page_start(string $title, string $active = ''): void
{
    $db = atdt_db();
    $settings = atdt_settings();
    $pending = (int)$db->val("SELECT COUNT(*) FROM users WHERE status = 'pending'");
    $paged = trim((string)($settings->get('sysop_paged_by', '') ?? ''));
    $board = (string)$settings->get('board_name', 'ATDT');
    $nav = [
        'index' => 'Dashboard',
        'settings' => 'Settings',
        'users' => 'Users',
        'areas' => 'Areas',
        'menus' => 'Menus',
        'community' => 'Community',
        'themes' => 'Themes',
        'editor' => 'ANSI Editor',
        'gallery' => 'Gallery',
        'doors' => 'Doors',
        'import' => 'Import',
        'logs' => 'Logs',
    ];
    // DOS doors ride behind their master switch (1.7.0): a board that
    // has not opted in never shows the page at all.
    if (\Atdt\Dos\Doors::on($settings)) {
        $nav = array_slice($nav, 0, 10, true)
            + ['dosdoors' => 'DOS Doors']
            + array_slice($nav, 10, null, true);
    }
    header('Content-Type: text/html; charset=utf-8');
    header('X-Frame-Options: SAMEORIGIN');
    header('Cache-Control: no-store');
    echo '<!doctype html><html lang="en"><head><meta charset="utf-8">';
    echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
    echo '<title>' . h($title) . ' - ' . h($board) . ' console</title>';
    echo '<link rel="stylesheet" href="../' . h(atdt_asset('assets/admin.css')) . '"></head>';
    echo '<body class="console">';
    echo '<header class="topbar"><div class="brand"><strong>' . h($board) . '</strong> <span class="muted">SysOp console</span></div><nav>';
    foreach ($nav as $slug => $label) {
        $badge = '';
        if ($slug === 'users' && $pending > 0) {
            $badge = ' <span class="badge">' . $pending . '</span>';
        }
        if ($slug === 'index' && $paged !== '') {
            $badge = ' <span class="badge page">page</span>';
        }
        $cls = $slug === $active ? ' class="active"' : '';
        echo '<a' . $cls . ' href="' . h($slug) . '.php">' . h($label) . $badge . '</a>';
    }
    echo '</nav><div class="who">' . h((string)(admin_user()['handle'] ?? '')) . ' · <a href="logout.php">log off</a></div></header>';
    echo '<main class="console">';
    // The offline switch (1.7.0) is easy to forget: every console page
    // says so out loud until it is flipped back.
    if ((string)$settings->get('board_offline', '0') === '1') {
        echo '<div class="flash bad">The board is offline - new callers are being turned away. '
            . '<a href="settings.php#offline">Flip the switch</a> when the work is done.</div>';
    }
    if ($f = flash_take()) {
        echo '<div class="flash ' . ($f[0] === 'ok' ? 'ok' : 'bad') . '">' . h($f[1]) . '</div>';
    }
    echo '<h1>' . h($title) . '</h1>';
}

function admin_page_end(): void
{
    echo '</main><footer>ATDT ' . h(ATDT_VERSION) . ' SysOp console. The board answers to you.</footer></body></html>';
}
