<?php

declare(strict_types=1);

/**
 * DOS door maintenance (SPEC-DOSDOORS §13): the machine, booted on the
 * LIVE game drive with the SysOp's own command in DOOR.BAT, driven from
 * the console with the machine's real screen and keyboard. This is how
 * a SysOp runs LORDCFG.EXE, an installer, or a plain DOS prompt against
 * the directory callers actually play in, without ever leaving the
 * browser. The page is chrome; the work happens in assets/dosmaint.js
 * against the ajax endpoints below, which speak the same dosfs ops as
 * api.php but ride the console's own login and CSRF token instead of a
 * BBS session (a SysOp at the console is not logged into the board).
 */

require __DIR__ . '/_lib.php';
$admin = admin_require();
$db = atdt_db();
$settings = atdt_settings();

// ---- the ajax side: what assets/dosmaint.js talks to ----------------------
// Every call is a POST (so the central CSRF check above has already run)
// with the payload as JSON in the 'body' field, and answers plain JSON.
$ajax = (string)($_GET['ajax'] ?? '');
if ($ajax !== '') {
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');
    $say = function (array $out): never {
        echo json_encode($out);
        exit;
    };
    if (!\Atdt\Dos\Doors::on($settings)) {
        http_response_code(404);
        $say(['ok' => false, 'error' => 'not_found']);
    }
    if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
        http_response_code(405);
        $say(['ok' => false, 'error' => 'post_only']);
    }
    $in = json_decode((string)($_POST['body'] ?? ''), true);
    $in = is_array($in) ? $in : [];

    if ($ajax === 'maint_open') {
        $slug = (string)($in['slug'] ?? '');
        $r = \Atdt\Dos\Sessions::openMaint($db, $settings, $admin, $slug, (string)($in['command'] ?? ''));
        if ($r['ok']) {
            admin_log("Opened a maintenance session on DOS door $slug");
        }
        $say($r);
    }

    $sess = \Atdt\Dos\Sessions::byToken($db, (string)($in['t'] ?? ''), (int)$admin['id']);
    if ($sess === null) {
        http_response_code(403);
        $say(['ok' => false, 'error' => 'bad_token']);
    }

    switch ($ajax) {
        case 'maint_ping':
            $say(['ok' => true, 'minutes' => \Atdt\Dos\Sessions::ping($db, $sess),
                'gen' => \Atdt\Dos\Fs::gen((string)$sess['slug'])]);

        case 'maint_close':
            \Atdt\Dos\Sessions::close($db, (string)$sess['token']);
            admin_log('Closed the maintenance session on DOS door ' . (string)$sess['slug']);
            $say(['ok' => true]);

        case 'maint_fs':
            // The same file ops api.php serves callers, on the same jail,
            // locks, and generation counter - one op or a batch of them.
            $slug = (string)$sess['slug'];
            $sid = (int)$sess['id'];
            $db->q('UPDATE dos_sessions SET last_seen = ? WHERE id = ?', [now_utc(), $sid]);
            if (is_array($in['ops'] ?? null)) {
                $results = [];
                foreach (array_slice($in['ops'], 0, 64) as $one) {
                    if (!is_array($one)) {
                        continue;
                    }
                    $results[] = \Atdt\Dos\Fs::op($db, $slug, $sid, (string)($one['op'] ?? ''), $one);
                }
                $say(['ok' => true, 'results' => $results]);
            }
            $say(\Atdt\Dos\Fs::op($db, $slug, $sid, (string)($in['op'] ?? ''), $in));
    }
    http_response_code(400);
    $say(['ok' => false, 'error' => 'bad_request']);
}

// ---- the page --------------------------------------------------------------

if (!\Atdt\Dos\Doors::on($settings)) {
    admin_page_start('Maintenance', 'dosdoors');
    echo '<div class="card"><h2>DOS doors are switched off</h2>'
        . '<p>Turn the system on first: <a href="settings.php">Settings</a> → Access → <strong>DOS doors</strong>.</p></div>';
    admin_page_end();
    exit;
}

$slug = (string)($_GET['door'] ?? '');
$door = \Atdt\Dos\Doors::bySlug($db, $slug);
if ($door === null) {
    flash_set('err', 'That door is not registered. Scan first; maintenance works on registered doors.');
    admin_redirect('dosdoors.php');
}
$live = (int)$db->val(
    'SELECT COUNT(*) FROM dos_sessions WHERE slug = ? AND node > 0 AND last_seen >= ?',
    [$slug, gmdate('Y-m-d\TH:i:s\Z', time() - \Atdt\Dos\Sessions::STALE_SECONDS)]
);

admin_page_start('Maintenance: ' . (string)$door['row']['title'], 'dosdoors');
?>

<div class="card">
  <p class="hint">This boots the same FreeDOS machine callers get, in this tab, on
  <strong>the live game drive</strong> of <code><?= h($slug) ?></code> as <code>D:\</code>,
  and runs the command you give it. The machine's own screen and keyboard are yours: this is
  where a door's config program (<code>LORDCFG.EXE</code>, <code>AASETUP.EXE</code>) or an
  installer runs, no DOSBox needed. When the command finishes you land at a real DOS prompt
  on <code>D:\</code> and can keep typing: <code>DIR</code>, <code>TYPE START.BAT</code>,
  <code>DEL</code>, another tool.</p>
  <p class="hint"><strong>Changes land in the live game immediately.</strong> Callers keep
  their seats while you work, so on a busy door, disable it on the
  <a href="dosdoors.php?edit=<?= (int)$door['row']['id'] ?>">DOS Doors page</a> first
  (maintenance still works on a disabled door). A Reset afterwards would undo your changes
  by reseeding from <code>files/</code>; to keep them across resets, copy the changed files
  back into <code>dosdoors/<?= h($slug) ?>/files/</code> on the server.</p>
  <?php if ($live > 0): ?>
  <p class="warn"><strong><?= $live ?> caller<?= $live === 1 ? ' is' : 's are' ?> in this door
  right now.</strong> Configuring under a live game risks the two machines writing the same
  file. Better: disable the door, wait for the seats to drain, then come back.</p>
  <?php endif; ?>
</div>

<div class="card">
  <label>Command to run at boot, on <code>D:\</code>
    <input type="text" id="maint_cmd" autocomplete="off" spellcheck="false"
           placeholder="LORDCFG.EXE (blank runs DIR and leaves you at the prompt)"></label>
  <p>
    <button class="btn" id="maint_start">Boot the machine</button>
    <button class="btn danger" id="maint_stop" disabled>End session</button>
    <a class="btn secondary" href="dosdoors.php?edit=<?= (int)$door['row']['id'] ?>">Back to the door</a>
  </p>
  <p class="hint" id="maint_status">Not running. Type a command (or leave it blank for a DOS
  prompt) and boot.</p>
</div>

<div class="card">
  <div id="maint_screen" style="display:inline-block; background:#000; padding:12px; border:1px solid #333; min-width:660px; min-height:200px;">
    <div style="white-space:pre; font:14px/14px 'Courier New',Courier,monospace; color:#ccc;"></div>
    <canvas style="display:none"></canvas>
  </div>
  <p class="hint">Keys typed on this page go to the machine (the command box above stays a
  normal box). Full DOS behavior applies: Ctrl+C interrupts a batch file, and a program that
  says press a key means the machine's keyboard, so click anywhere outside the command box
  first if keys seem to go nowhere. A machine quiet for a minute dozes to spare your laptop;
  any key or click wakes it where it left off.</p>
</div>

<script>
var ATDT_MAINT = {
    slug: <?= json_encode($slug) ?>,
    csrf: <?= json_encode(admin_csrf_token()) ?>,
    base: "../dosdoors/"
};
</script>
<script src="../<?= h(atdt_asset('dosdoors/client/libv86.js')) ?>"></script>
<script src="../<?= h(atdt_asset('assets/dosdoor.js')) ?>"></script>
<script src="../<?= h(atdt_asset('assets/dosmaint.js')) ?>"></script>

<?php admin_page_end(); ?>
