<?php

declare(strict_types=1);

/**
 * The menu designer (SPEC §11.5). Left: menu list. Main column, full
 * width so nothing needs a horizontal scrollbar (owner round 4): the
 * selected menu's items, the add/edit form (action + argument dropdowns
 * explain themselves), and the live preview of the caller terminal
 * rendering this menu.
 */

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

use Atdt\Screens\MenuScreen;

const ACTIONS = ['GOTO', 'MODULE', 'DOOR', 'ART', 'HELP', 'EMAILSYSOP', 'BACK', 'LOGOFF'];

// The DOSDOOR action exists only while the master switch is on (§13):
// off, the designer neither offers it nor accepts it.
$dosOn = \Atdt\Dos\Doors::on(atdt_settings());
$actions = ACTIONS;
if ($dosOn) {
    array_splice($actions, 3, 0, ['DOSDOOR']);
}

/** What each action does; shown in the reference card + picker hints. */
const ACTION_INFO = [
    'GOTO' => 'open another menu (pick which one)',
    'MODULE' => 'open a built-in part of the board (pick which below)',
    'DOOR' => 'launch a door game',
    'DOSDOOR' => 'launch a DOS door game in the caller\'s browser',
    'ART' => 'show a full-screen gallery piece, any key returns',
    'HELP' => 'show the caller guide to pipe colors and MCI codes',
    'EMAILSYSOP' => 'let the caller email the SysOp (argument = the address)',
    'BACK' => 'go back to the previous menu',
    'LOGOFF' => 'hang up (asks the caller to confirm)',
];

/** Every MODULE argument, in plain words. */
const MODULE_INFO = [
    'mailbox' => 'E-mail: the private mail room',
    'areachange' => 'Message areas: pick which board to read',
    'read' => 'Read messages: the threaded message list',
    'post' => 'Post a message in the current area',
    'newscan' => 'New scan: read everything unread, area by area',
    'oneliners' => 'Oneliners: the graffiti wall',
    'rumors' => 'Rumors: the rumor mill',
    'automsg' => 'Auto-message: the one-board-wide wall message',
    'voting' => 'Voting booth: questions and bar-graph results',
    'bbslist' => 'BBS list: the other-boards directory',
    'bulletins' => 'Bulletins: SysOp announcements',
    'userlist' => 'User list: who has an account here',
    'lastcallers' => 'Last callers: who called recently',
    'whosonline' => "Who's online: who is on the wire right now",
    'stats' => 'Board stats: totals and top posters',
    'usersettings' => 'Your settings: bell, speed, password',
    'chat' => 'Chat: the live wire, everyone together',
    'pagesysop' => 'Page sysop: ring the back-room bell',
    'setarea' => "Set area: change this menu's current area to a named one, staying here",
    'setareaback' => 'Set area and return: pick the named area, then step back a menu',
];

if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
    $menuName = strtoupper(trim((string)($_POST['m'] ?? '')));
    $menu = $db->one('SELECT * FROM menus WHERE name = ?', [$menuName]);
    $back = 'menus.php?m=' . rawurlencode($menuName);
    switch ((string)($_POST['action'] ?? '')) {
        case 'addmenu':
            $name = strtoupper(trim((string)($_POST['name'] ?? '')));
            $title = trim((string)($_POST['title'] ?? ''));
            if (!preg_match('/^[A-Z0-9_]{2,20}$/', $name)) {
                flash_set('err', 'Menu name: 2-20 chars, A-Z 0-9 _ only.');
                admin_redirect('menus.php');
            }
            if ($db->one('SELECT id FROM menus WHERE name = ?', [$name]) !== null) {
                flash_set('err', "Menu $name already exists.");
                admin_redirect('menus.php');
            }
            $db->insert('menus', [
                'name' => $name,
                'title' => $title === '' ? ucwords(strtolower($name)) : mb_strimwidth($title, 0, 40),
                'art' => '', 'template' => '', 'fallback_generate' => 1,
            ]);
            admin_log("Added menu $name");
            flash_set('ok', "Menu $name added. Now give it items.");
            admin_redirect('menus.php?m=' . rawurlencode($name));

        case 'savemenu':
            if ($menu === null) {
                admin_redirect('menus.php');
            }
            $artPick = preg_match('/^[a-z0-9_-]{0,32}$/', (string)($_POST['art'] ?? '')) ? (string)$_POST['art'] : '';
            // Area scope (1.6.5, SPEC 7.2): blank means legacy, always.
            $scope = strtoupper(trim((string)($_POST['area_scope'] ?? '')));
            if ($scope !== '' && !preg_match(\Atdt\AreaScope::SCOPE_PATTERN, $scope)) {
                flash_set('err', 'Area scope: 1-16 chars, A-Z 0-9 _ only (or blank for normal behavior).');
                admin_redirect($back);
            }
            $defArea = strtoupper(trim((string)($_POST['default_area'] ?? '')));
            if ($scope === '') {
                $defArea = ''; // meaningless without a scope (SPEC 3.1)
            } elseif ($defArea !== '' && \Atdt\AreaScope::byTag($db, $defArea) === null) {
                flash_set('err', "Default area: no area is tagged $defArea.");
                admin_redirect($back);
            }
            $db->q(
                'UPDATE menus SET title = ?, art = ?, area_scope = ?, default_area = ? WHERE id = ?',
                [
                    mb_strimwidth(trim((string)($_POST['title'] ?? '')), 0, 40),
                    $artPick,
                    $scope === '' ? null : $scope,
                    $defArea === '' ? null : $defArea,
                    (int)$menu['id'],
                ]
            );
            // The non-blocking nudge (SPEC 7.2): a scoped section with no
            // default greets first-timers with the area picker.
            if ($scope !== '' && $defArea === '') {
                $blankMsg = (int)$db->val(
                    "SELECT COUNT(*) FROM menu_items WHERE menu_id = ? AND action = 'MODULE'
                       AND LOWER(arg) IN ('read', 'post', 'newscan') AND (arg2 IS NULL OR arg2 = '')",
                    [(int)$menu['id']]
                );
                if ($blankMsg > 0) {
                    flash_set('ok', "Menu $menuName saved. Callers entering this section will be shown the"
                        . ' area picker before their first read. Set a Default area to skip it.');
                    admin_log("Edited menu $menuName");
                    admin_redirect($back);
                }
            }
            admin_log("Edited menu $menuName");
            // 1.5.5 (owner report): picking "generated" LOOKED broken when
            // a per-level variant kept overriding it. Say so at save time.
            $variantN = (int)$db->val('SELECT COUNT(*) FROM menu_art WHERE menu_id = ?', [(int)$menu['id']]);
            if ($artPick === '' && $variantN > 0) {
                flash_set('ok', "Menu $menuName saved -- but $variantN per-level variant backdrop"
                    . ($variantN === 1 ? '' : 's')
                    . ' still override the generated layout for callers who match. Remove them below to show the generated menu.');
            } else {
                flash_set('ok', "Menu $menuName saved.");
            }
            admin_redirect($back);

        case 'delmenu':
            if ($menu === null || $menuName === 'MAIN') {
                flash_set('err', 'MAIN stays. It is where callers land.');
                admin_redirect('menus.php');
            }
            $db->q('DELETE FROM menu_items WHERE menu_id = ?', [(int)$menu['id']]);
            $db->q('DELETE FROM menus WHERE id = ?', [(int)$menu['id']]);
            admin_log("Deleted menu $menuName");
            flash_set('ok', "Menu $menuName deleted. GOTOs pointing at it fail soft to MAIN.");
            admin_redirect('menus.php');

        case 'additem':
        case 'saveitem':
            if ($menu === null) {
                admin_redirect('menus.php');
            }
            $itemId = (int)($_POST['id'] ?? 0);
            $hotkey = strtoupper(substr(trim((string)($_POST['hotkey'] ?? '')), 0, 1));
            $label = mb_strimwidth(trim((string)($_POST['label'] ?? '')), 0, 40);
            $act = (string)($_POST['act'] ?? 'MODULE');
            $arg = trim((string)($_POST['arg'] ?? ''));
            if ($hotkey === '' || !preg_match('/^[A-Z0-9]$/', $hotkey)) {
                flash_set('err', 'Hotkey: one letter or digit.');
                admin_redirect($back);
            }
            if ($label === '') {
                flash_set('err', 'Every item needs a label.');
                admin_redirect($back);
            }
            if (!in_array($act, $actions, true)) {
                $act = 'MODULE';
            }
            if (in_array($act, ['BACK', 'LOGOFF', 'HELP'], true)) {
                $arg = '';
            } elseif ($act === 'EMAILSYSOP') {
                // 1.5.0: the argument is a mailbox, not a slug.
                if (filter_var($arg, FILTER_VALIDATE_EMAIL) === false) {
                    flash_set('err', 'EMAILSYSOP needs an email address as its argument.');
                    admin_redirect($back);
                }
            } elseif ($arg === '' || !preg_match('/^[A-Za-z0-9_-]{1,32}$/', $arg)) {
                flash_set('err', "$act needs an argument (letters, digits, _ or -).");
                admin_redirect($back);
            }
            $dupe = $db->one(
                'SELECT id FROM menu_items WHERE menu_id = ? AND hotkey = ?',
                [(int)$menu['id'], $hotkey]
            );
            if ($dupe !== null && (int)$dupe['id'] !== $itemId) {
                flash_set('err', "Hotkey $hotkey is already taken on $menuName.");
                admin_redirect($back);
            }
            // The second argument (1.6.5, SPEC 7.3): an area tag for the
            // message modules, a category name for areachange, nothing
            // for anything else. setarea without an area is the one
            // blocking refusal.
            $module = $act === 'MODULE' ? strtolower($arg) : '';
            $arg2 = mb_strimwidth(trim((string)($_POST['arg2'] ?? '')), 0, \Atdt\AreaScope::ARG2_MAX_LEN);
            if (in_array($module, ['read', 'post', 'newscan', 'setarea', 'setareaback'], true)) {
                $arg2 = strtoupper($arg2);
                if (($module === 'setarea' || $module === 'setareaback') && $arg2 === '') {
                    flash_set('err', "$module needs an area picked. It has nothing else to do.");
                    admin_redirect($back);
                }
                if ($arg2 !== '' && \Atdt\AreaScope::byTag($db, $arg2) === null) {
                    flash_set('err', "No area is tagged $arg2.");
                    admin_redirect($back);
                }
            } elseif ($module === 'areachange') {
                if ($arg2 !== '' && $db->one(
                    'SELECT id FROM message_categories WHERE LOWER(name) = LOWER(?)',
                    [$arg2]
                ) === null) {
                    flash_set('err', "No category is named \"$arg2\".");
                    admin_redirect($back);
                }
            } else {
                $arg2 = ''; // hidden for every other action; keep rows clean
            }
            $fields = [
                'hotkey' => $hotkey,
                'label' => $label,
                'acs' => trim((string)($_POST['acs'] ?? '')),
                'action' => $act,
                'arg' => $arg,
                'arg2' => $arg2 === '' ? null : $arg2,
                'hidden' => isset($_POST['hidden']) ? 1 : 0,
            ];
            if ($itemId > 0) {
                $db->q(
                    'UPDATE menu_items SET hotkey = ?, label = ?, acs = ?, action = ?, arg = ?, arg2 = ?, hidden = ?
                     WHERE id = ? AND menu_id = ?',
                    [...array_values($fields), $itemId, (int)$menu['id']]
                );
            } else {
                $fields['menu_id'] = (int)$menu['id'];
                $fields['sort'] = 1 + (int)$db->val(
                    'SELECT COALESCE(MAX(sort), 0) FROM menu_items WHERE menu_id = ?',
                    [(int)$menu['id']]
                );
                $db->insert('menu_items', $fields);
            }
            admin_log("Edited items on menu $menuName");
            flash_set('ok', "Item [$hotkey] saved.");
            admin_redirect($back);

        case 'artadd':
            // Per-level backdrops (M7): first matching ACS in sort order wins.
            if ($menu === null) {
                admin_redirect('menus.php');
            }
            $slot = strtolower(trim((string)($_POST['slot'] ?? '')));
            if (!preg_match('/^[a-z0-9_-]{1,32}$/', $slot)) {
                flash_set('err', 'Variant art slot: 1-32 chars, a-z 0-9 _ - only.');
                admin_redirect($back);
            }
            $db->insert('menu_art', [
                'menu_id' => (int)$menu['id'],
                'acs' => trim((string)($_POST['acs'] ?? '')),
                'art' => $slot,
                'sort' => 1 + (int)$db->val('SELECT COALESCE(MAX(sort), 0) FROM menu_art WHERE menu_id = ?', [(int)$menu['id']]),
            ]);
            admin_log("Added backdrop variant '$slot' to menu $menuName");
            flash_set('ok', "Variant added - callers passing its ACS now see '$slot'.");
            admin_redirect($back);

        case 'artdel':
            if ($menu !== null) {
                $db->q('DELETE FROM menu_art WHERE id = ? AND menu_id = ?', [(int)($_POST['id'] ?? 0), (int)$menu['id']]);
                admin_log("Removed a backdrop variant from menu $menuName");
                flash_set('ok', 'Variant removed.');
            }
            admin_redirect($back);

        case 'artmove':
            $variant = $db->one('SELECT * FROM menu_art WHERE id = ?', [(int)($_POST['id'] ?? 0)]);
            if ($menu !== null && $variant !== null && (int)$variant['menu_id'] === (int)$menu['id']) {
                $dirn = (string)($_POST['dir'] ?? '') === 'up' ? -1 : 1;
                $swap = $db->one(
                    'SELECT * FROM menu_art WHERE menu_id = ? AND sort ' . ($dirn < 0 ? '<' : '>') . ' ?
                     ORDER BY sort ' . ($dirn < 0 ? 'DESC' : 'ASC') . ' LIMIT 1',
                    [(int)$menu['id'], (int)$variant['sort']]
                );
                if ($swap !== null) {
                    $db->q('UPDATE menu_art SET sort = ? WHERE id = ?', [(int)$swap['sort'], (int)$variant['id']]);
                    $db->q('UPDATE menu_art SET sort = ? WHERE id = ?', [(int)$variant['sort'], (int)$swap['id']]);
                }
            }
            admin_redirect($back);

        case 'moveitem':
            $item = $db->one('SELECT * FROM menu_items WHERE id = ?', [(int)($_POST['id'] ?? 0)]);
            if ($menu !== null && $item !== null && (int)$item['menu_id'] === (int)$menu['id']) {
                $dirn = (string)($_POST['dir'] ?? '') === 'up' ? -1 : 1;
                $swap = $db->one(
                    'SELECT * FROM menu_items WHERE menu_id = ? AND sort ' . ($dirn < 0 ? '<' : '>') . ' ?
                     ORDER BY sort ' . ($dirn < 0 ? 'DESC' : 'ASC') . ' LIMIT 1',
                    [(int)$menu['id'], (int)$item['sort']]
                );
                if ($swap !== null) {
                    $db->q('UPDATE menu_items SET sort = ? WHERE id = ?', [(int)$swap['sort'], (int)$item['id']]);
                    $db->q('UPDATE menu_items SET sort = ? WHERE id = ?', [(int)$item['sort'], (int)$swap['id']]);
                }
            }
            admin_redirect($back);

        case 'delitem':
            if ($menu !== null) {
                $db->q('DELETE FROM menu_items WHERE id = ? AND menu_id = ?', [(int)($_POST['id'] ?? 0), (int)$menu['id']]);
                admin_log("Removed an item from menu $menuName");
                flash_set('ok', 'Item removed.');
            }
            admin_redirect($back);
    }
    admin_redirect('menus.php');
}

$menus = $db->all('SELECT m.*, (SELECT COUNT(*) FROM menu_items i WHERE i.menu_id = m.id) AS items FROM menus m ORDER BY m.id');
$current = strtoupper((string)($_GET['m'] ?? 'MAIN'));
$menu = $db->one('SELECT * FROM menus WHERE name = ?', [$current]) ?? $db->one('SELECT * FROM menus WHERE name = ?', ['MAIN']);
$current = (string)($menu['name'] ?? 'MAIN');
$items = $menu === null ? [] : $db->all('SELECT * FROM menu_items WHERE menu_id = ? ORDER BY sort, id', [(int)$menu['id']]);
$variants = $menu === null ? [] : $db->all('SELECT * FROM menu_art WHERE menu_id = ? ORDER BY sort, id', [(int)$menu['id']]);
$editItem = null;
foreach ($items as $i) {
    if ((int)$i['id'] === (int)($_GET['item'] ?? 0)) {
        $editItem = $i;
    }
}
$menuNames = array_column($menus, 'name');
$doorSlugs = array_column($db->all('SELECT slug FROM doors ORDER BY slug'), 'slug');
$dosDoorSlugs = $dosOn
    ? array_column($db->all('SELECT slug FROM dos_doors WHERE enabled = 1 ORDER BY sort, slug'), 'slug')
    : [];
// Backdrops come from the board-wide art gallery (1.1.0).
$artSlots = \Atdt\Art::pieces();
// 1.6.5: every local area (the sysop sees unlisted ones too, badged),
// in the caller-side order, for the targeting selects; and every
// category name for the areachange filter.
$allAreas = $db->all(
    'SELECT a.tag, a.name, a.listed, c.name AS cat_name
     FROM message_areas a
     LEFT JOIN message_categories c ON c.id = a.category_id
     WHERE a.kind = ?
     ORDER BY COALESCE(c.sort, 2147483647), COALESCE(c.id, 2147483647), a.sort, a.id',
    ['local']
);
$catNames = array_column($db->all('SELECT name FROM message_categories ORDER BY sort, id'), 'name');
$areaOptionLabel = static fn(array $a): string => (string)$a['tag'] . ' - ' . (string)$a['name']
    . ((int)($a['listed'] ?? 1) === 0 ? ' (unlisted)' : '');

admin_page_start('Menus', 'menus');
?>

<datalist id="args-GOTO"><?php foreach ($menuNames as $n): ?><option value="<?= h((string)$n) ?>"><?php endforeach; ?></datalist>
<datalist id="args-MODULE"><?php foreach (MenuScreen::moduleKeys() as $n): ?><option value="<?= h($n) ?>"><?php endforeach; ?></datalist>
<datalist id="args-DOOR"><?php foreach ($doorSlugs as $n): ?><option value="<?= h((string)$n) ?>"><?php endforeach; ?></datalist>
<datalist id="args-DOSDOOR"><?php foreach ($dosDoorSlugs as $n): ?><option value="<?= h((string)$n) ?>"><?php endforeach; ?></datalist>
<datalist id="args-ART"><?php foreach ($artSlots as $n): ?><option value="<?= h($n) ?>"><?php endforeach; ?></datalist>

<div class="menucols">
<div class="menulist">
  <div class="card">
    <h2>Menus</h2>
    <?php foreach ($menus as $m): ?>
      <div class="menurow<?= (string)$m['name'] === $current ? ' active' : '' ?>">
        <a href="menus.php?m=<?= rawurlencode((string)$m['name']) ?>"><?= h((string)$m['name']) ?></a>
        <span class="hint"><?= (int)$m['items'] ?> items<?=
            trim((string)($m['area_scope'] ?? '')) !== '' ? ' · scope ' . h((string)$m['area_scope']) : '' ?></span>
      </div>
    <?php endforeach; ?>
    <form method="post" class="row" style="margin-top:10px">
      <?= csrf_field() ?>
      <input type="hidden" name="action" value="addmenu">
      <input type="text" name="name" placeholder="NEWMENU" size="10" style="text-transform:uppercase">
      <button class="btn small">Add</button>
    </form>
  </div>

  <div class="card">
    <h2><?= h($current) ?></h2>
    <form method="post">
      <?= csrf_field() ?>
      <input type="hidden" name="action" value="savemenu">
      <input type="hidden" name="m" value="<?= h($current) ?>">
      <label>Title <input type="text" name="title" value="<?= h((string)($menu['title'] ?? '')) ?>"></label>
      <label>Backdrop <span class="hint">(a gallery piece; blank = generated layout)</span>
        <select name="art">
          <option value="">(generated layout)</option>
          <?php foreach ($artSlots as $n): ?>
          <option value="<?= h($n) ?>"<?= (string)($menu['art'] ?? '') === $n ? ' selected' : '' ?>><?= h($n) ?></option>
          <?php endforeach; ?>
          <?php if (($menu['art'] ?? '') !== '' && $menu['art'] !== null && !in_array((string)$menu['art'], $artSlots, true)): ?>
          <option value="<?= h((string)$menu['art']) ?>" selected><?= h((string)$menu['art']) ?> (missing)</option>
          <?php endif; ?>
        </select></label>
      <?php if ($variants !== []): ?>
      <p class="hint">The <?= count($variants) ?> per-level variant<?= count($variants) === 1 ? '' : 's' ?>
      below outrank this backdrop for callers who match -- including the generated layout.
      To show the generated menu to everyone, remove the variants too.</p>
      <?php endif; ?>
      <?php $curScope = trim((string)($menu['area_scope'] ?? '')); ?>
      <details<?= $curScope !== '' ? ' open' : '' ?>>
        <summary class="hint">Area scope (advanced)</summary>
        <label>Area scope
          <input type="text" name="area_scope" id="scopein" maxlength="16" size="12"
            style="text-transform:uppercase" value="<?= h($curScope) ?>"></label>
        <p class="hint">Leave blank for normal behavior: this menu shares the
        board's current message area with every other menu. Give it a name
        (DE, SUZ, LOUNGE) and this menu keeps its own current area, separate
        from the rest of the board. Menus sharing a name share a current
        area.</p>
        <label>Default area
          <select name="default_area" id="defareasel"<?= $curScope === '' ? ' disabled' : '' ?>>
            <option value="">(none)</option>
            <?php foreach ($allAreas as $aa): ?>
            <option value="<?= h((string)$aa['tag']) ?>"<?= strtoupper(trim((string)($menu['default_area'] ?? ''))) === (string)$aa['tag'] ? ' selected' : '' ?>><?= h($areaOptionLabel($aa)) ?></option>
            <?php endforeach; ?>
          </select></label>
        <p class="hint">The area a caller starts in the first time they enter
        this section. If blank, the first read or post here shows the area
        picker.</p>
      </details>
      <button class="btn small">Save</button>
    </form>
    <?php if ($current !== 'MAIN'): ?>
    <form method="post" onsubmit="return confirm('Delete menu <?= h($current) ?> and its items?');">
      <?= csrf_field() ?>
      <input type="hidden" name="action" value="delmenu">
      <input type="hidden" name="m" value="<?= h($current) ?>">
      <button class="btn small danger" style="margin-top:8px">Delete menu</button>
    </form>
    <?php endif; ?>
  </div>

  <div class="card">
    <h2>Backdrops by level</h2>
    <p class="hint">The first row whose ACS the caller passes becomes their backdrop for
    <?= h($current) ?>; SysOps can get one screen, regulars another. No match falls back
    to the backdrop above. Draw pieces in the <a href="editor.php">ANSI editor</a>'s gallery.</p>
    <?php if ($variants !== []): ?>
    <table class="list">
      <?php foreach ($variants as $vi => $v): ?>
      <tr>
        <td class="nowrap">
          <form method="post" class="inline"><?= csrf_field() ?>
            <input type="hidden" name="action" value="artmove"><input type="hidden" name="m" value="<?= h($current) ?>">
            <input type="hidden" name="id" value="<?= (int)$v['id'] ?>"><input type="hidden" name="dir" value="up">
            <button class="btn small secondary" <?= $vi === 0 ? 'disabled' : '' ?>>↑</button></form>
          <form method="post" class="inline"><?= csrf_field() ?>
            <input type="hidden" name="action" value="artmove"><input type="hidden" name="m" value="<?= h($current) ?>">
            <input type="hidden" name="id" value="<?= (int)$v['id'] ?>"><input type="hidden" name="dir" value="down">
            <button class="btn small secondary" <?= $vi === count($variants) - 1 ? 'disabled' : '' ?>>↓</button></form>
        </td>
        <td><code><?= h((string)$v['art']) ?></code></td>
        <td><code><?= h((string)$v['acs']) ?: '<span class="hint">everyone</span>' ?></code></td>
        <td>
          <form method="post" class="inline"><?= csrf_field() ?>
            <input type="hidden" name="action" value="artdel"><input type="hidden" name="m" value="<?= h($current) ?>">
            <input type="hidden" name="id" value="<?= (int)$v['id'] ?>">
            <button class="btn small danger">×</button></form>
        </td>
      </tr>
      <?php endforeach; ?>
    </table>
    <?php endif; ?>
    <form method="post" class="row" style="margin-top:8px">
      <?= csrf_field() ?>
      <input type="hidden" name="action" value="artadd">
      <input type="hidden" name="m" value="<?= h($current) ?>">
      <select name="slot">
        <option value="">(pick a gallery piece)</option>
        <?php foreach ($artSlots as $n): ?>
        <option value="<?= h($n) ?>"><?= h($n) ?></option>
        <?php endforeach; ?>
      </select>
      <input type="text" name="acs" placeholder="ACS (s255)" size="9">
      <button class="btn small">Add</button>
    </form>
  </div>
</div>

<div>
  <div class="card">
    <h2>Items on <?= h($current) ?></h2>
    <table class="list">
      <tr><th></th><th>Key</th><th>Label</th><th>Action</th><th>Arg</th><th>ACS</th><th>Hidden</th><th></th></tr>
      <?php foreach ($items as $i => $it): ?>
      <tr<?= $editItem !== null && (int)$editItem['id'] === (int)$it['id'] ? ' class="pending"' : '' ?>>
        <td class="nowrap">
          <form method="post" class="inline"><?= csrf_field() ?>
            <input type="hidden" name="action" value="moveitem"><input type="hidden" name="m" value="<?= h($current) ?>">
            <input type="hidden" name="id" value="<?= (int)$it['id'] ?>"><input type="hidden" name="dir" value="up">
            <button class="btn small secondary" <?= $i === 0 ? 'disabled' : '' ?>>↑</button></form>
          <form method="post" class="inline"><?= csrf_field() ?>
            <input type="hidden" name="action" value="moveitem"><input type="hidden" name="m" value="<?= h($current) ?>">
            <input type="hidden" name="id" value="<?= (int)$it['id'] ?>"><input type="hidden" name="dir" value="down">
            <button class="btn small secondary" <?= $i === count($items) - 1 ? 'disabled' : '' ?>>↓</button></form>
        </td>
        <td><code>[<?= h((string)$it['hotkey']) ?>]</code></td>
        <td><?= h((string)$it['label']) ?></td>
        <td><?= h((string)$it['action']) ?></td>
        <td><code><?= h((string)$it['arg']) ?></code><?=
            trim((string)($it['arg2'] ?? '')) !== '' ? ' <span class="hint">@ ' . h((string)$it['arg2']) . '</span>' : '' ?></td>
        <td><code><?= h((string)$it['acs']) ?></code></td>
        <td><?= (int)$it['hidden'] === 1 ? 'yes' : '' ?></td>
        <td class="nowrap">
          <a href="menus.php?m=<?= rawurlencode($current) ?>&amp;item=<?= (int)$it['id'] ?>">edit</a>
          <form method="post" class="inline"><?= csrf_field() ?>
            <input type="hidden" name="action" value="delitem"><input type="hidden" name="m" value="<?= h($current) ?>">
            <input type="hidden" name="id" value="<?= (int)$it['id'] ?>">
            <button class="btn small danger">×</button></form>
        </td>
      </tr>
      <?php endforeach; ?>
    </table>
  </div>

  <div class="card drawer">
    <h2><?= $editItem === null ? 'Add an item' : 'Edit item [' . h((string)$editItem['hotkey']) . ']' ?></h2>
    <form method="post" id="itemform">
      <?= csrf_field() ?>
      <input type="hidden" name="action" value="<?= $editItem === null ? 'additem' : 'saveitem' ?>">
      <input type="hidden" name="m" value="<?= h($current) ?>">
      <?php if ($editItem !== null): ?><input type="hidden" name="id" value="<?= (int)$editItem['id'] ?>"><?php endif; ?>
      <div class="row">
        <label>Hotkey <input type="text" name="hotkey" size="3" maxlength="1" style="text-transform:uppercase"
          value="<?= h((string)($editItem['hotkey'] ?? '')) ?>"></label>
        <label>Label <input type="text" name="label" maxlength="40" value="<?= h((string)($editItem['label'] ?? '')) ?>"></label>
      </div>
      <div class="row">
        <label>Action
          <select name="act" id="actsel">
            <?php foreach ($actions as $a): ?>
            <option value="<?= $a ?>"<?= (string)($editItem['action'] ?? 'MODULE') === $a ? ' selected' : '' ?>><?= $a ?>: <?= h(ACTION_INFO[$a]) ?></option>
            <?php endforeach; ?>
          </select></label>
      </div>
      <div class="row">
        <label style="flex:1">Argument <span class="hint" id="arghint"></span>
          <select id="argsel-GOTO" class="argsel" data-for="GOTO" style="display:none">
            <?php foreach ($menuNames as $n): ?><option value="<?= h((string)$n) ?>"><?= h((string)$n) ?></option><?php endforeach; ?>
          </select>
          <select id="argsel-MODULE" class="argsel" data-for="MODULE" style="display:none">
            <?php foreach (MODULE_INFO as $k => $desc): ?>
            <option value="<?= h($k) ?>"><?= h($k) ?>: <?= h($desc) ?></option>
            <?php endforeach; ?>
          </select>
          <select id="argsel-DOOR" class="argsel" data-for="DOOR" style="display:none">
            <?php foreach ($doorSlugs as $n): ?><option value="<?= h((string)$n) ?>"><?= h((string)$n) ?></option><?php endforeach; ?>
          </select>
          <?php if ($dosOn): ?>
          <select id="argsel-DOSDOOR" class="argsel" data-for="DOSDOOR" style="display:none">
            <?php foreach ($dosDoorSlugs as $n): ?><option value="<?= h((string)$n) ?>"><?= h((string)$n) ?></option><?php endforeach; ?>
          </select>
          <?php endif; ?>
          <select id="argsel-ART" class="argsel" data-for="ART" style="display:none">
            <?php foreach ($artSlots as $n): ?><option value="<?= h((string)$n) ?>"><?= h((string)$n) ?></option><?php endforeach; ?>
          </select>
          <input type="text" name="arg" id="argin" list="args-<?= h((string)($editItem['action'] ?? 'MODULE')) ?>"
            value="<?= h((string)($editItem['arg'] ?? '')) ?>">
        </label>
      </div>
      <div class="row" id="arg2row" style="display:none">
        <label style="flex:1"><span id="arg2label">Area</span> <span class="hint" id="arg2hint"></span>
          <select name="arg2" id="arg2sel" data-value="<?= h((string)($editItem['arg2'] ?? '')) ?>"></select>
        </label>
      </div>
      <div class="row">
        <label>ACS <span class="hint">(blank = everyone)</span>
          <input type="text" name="acs" value="<?= h((string)($editItem['acs'] ?? '')) ?>"></label>
        <label class="radio"><input type="checkbox" name="hidden"<?= (int)($editItem['hidden'] ?? 0) === 1 ? ' checked' : '' ?>>
          hidden <span class="hint">(works, not listed)</span></label>
      </div>
      <button class="btn"><?= $editItem === null ? 'Add item' : 'Save item' ?></button>
      <?php if ($editItem !== null): ?><a class="btn secondary" href="menus.php?m=<?= rawurlencode($current) ?>">Close</a><?php endif; ?>
    </form>
    <p class="hint">An item's ACS hides it from callers who don't pass; the same
    access strings as areas (full cheat-sheet on the Areas page). "Hidden" items
    work when typed but never show in the list.</p>
  </div>

  <div class="card">
    <h2>Live preview <span class="hint">as the current theme renders it</span></h2>
    <iframe class="preview" src="../index.php?preview=<?= rawurlencode($current) ?>" title="menu preview"></iframe>
    <p class="hint">The preview uses your console login; callers still see menus filtered by their own access. Save an item and it refreshes.</p>
  </div>
</div>
</div>

<script>
(function () {
  var sel = document.getElementById('actsel');
  var arg = document.getElementById('argin');
  var hint = document.getElementById('arghint');
  if (!sel) return;
  var hints = {
    GOTO: 'which menu to open', MODULE: 'which part of the board', DOOR: 'which door', DOSDOOR: 'which DOS door',
    ART: 'which gallery piece', HELP: 'nothing needed',
    EMAILSYSOP: 'the address the letters go to',
    BACK: 'nothing needed', LOGOFF: 'nothing needed'
  };
  var sels = {};
  document.querySelectorAll('select.argsel').forEach(function (s) {
    sels[s.getAttribute('data-for')] = s;
    s.addEventListener('change', function () { arg.value = s.value; syncArg2(); });
  });
  // The second argument (1.6.5): an area for read/post/newscan/setarea,
  // a category for areachange, hidden for everything else.
  var AREAS = <?= json_encode(array_map(
      fn(array $a) => ['tag' => (string)$a['tag'], 'label' => $areaOptionLabel($a)],
      $allAreas
  )) ?>;
  var CATS = <?= json_encode(array_values(array_map('strval', $catNames))) ?>;
  var arg2row = document.getElementById('arg2row');
  var arg2sel = document.getElementById('arg2sel');
  var arg2label = document.getElementById('arg2label');
  var arg2hint = document.getElementById('arg2hint');
  var arg2saved = arg2sel ? arg2sel.getAttribute('data-value') || '' : '';
  function fillArg2(options, keep) {
    arg2sel.innerHTML = '';
    options.forEach(function (o) {
      var opt = document.createElement('option');
      opt.value = o.value;
      opt.textContent = o.label;
      if (o.value === keep) { opt.selected = true; }
      arg2sel.appendChild(opt);
    });
  }
  function syncArg2() {
    if (!arg2row) return;
    var a = sel.value.split(' ')[0];
    var mod = a === 'MODULE' ? (arg.value || '').toLowerCase() : '';
    var setter = mod === 'setarea' || mod === 'setareaback';
    if (mod === 'read' || mod === 'post' || mod === 'newscan' || setter) {
      var opts = AREAS.map(function (ar) { return {value: ar.tag, label: ar.label}; });
      if (!setter) { opts.unshift({value: '', label: '(use current area)'}); }
      fillArg2(opts, arg2saved);
      arg2label.textContent = 'Area';
      arg2hint.textContent = setter ? '(required)' : '(blank follows the current area)';
      arg2row.style.display = '';
    } else if (mod === 'areachange') {
      var copts = [{value: '', label: '(all areas)'}].concat(
        CATS.map(function (cn) { return {value: cn, label: cn}; })
      );
      fillArg2(copts, arg2saved);
      arg2label.textContent = 'Limit to category';
      arg2hint.textContent = '';
      arg2row.style.display = '';
    } else {
      arg2row.style.display = 'none';
      if (arg2sel) { arg2sel.innerHTML = ''; }
    }
  }
  function sync() {
    var a = sel.value.split(' ')[0];
    Object.keys(sels).forEach(function (k) { sels[k].style.display = 'none'; });
    var picker = sels[a] && sels[a].options.length > 0 ? sels[a] : null;
    if (picker) {
      picker.style.display = '';
      // Adopt the current arg if the picker knows it, else push the picker's
      // first option into the field so what you see is what gets saved.
      var match = Array.prototype.find.call(picker.options, function (o) { return o.value === arg.value; });
      if (match) { picker.value = arg.value; } else { arg.value = picker.value; }
      arg.style.display = 'none';
    } else {
      arg.style.display = '';
      arg.setAttribute('list', 'args-' + a);
    }
    arg.disabled = (a === 'BACK' || a === 'LOGOFF' || a === 'HELP');
    if (arg.disabled) { arg.value = ''; arg.style.display = ''; }
    hint.textContent = '(' + hints[a] + ')';
    syncArg2();
  }
  sel.addEventListener('change', sync);
  arg.addEventListener('input', syncArg2);
  sync();
  // Default area only means something once a scope is named (SPEC 7.2).
  var scopein = document.getElementById('scopein');
  var defarea = document.getElementById('defareasel');
  if (scopein && defarea) {
    var syncScope = function () {
      defarea.disabled = scopein.value.trim() === '';
      if (defarea.disabled) { defarea.value = ''; }
    };
    scopein.addEventListener('input', syncScope);
  }
})();
</script>

<?php admin_page_end(); ?>
