<?php

declare(strict_types=1);

/**
 * Users (SPEC §11.3): search/sort table with pending applications up top
 * (Approve/Deny one-click), and an edit drawer per caller: level, flags,
 * status, time adjustments, note, theme, password reset, soft delete.
 */

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

if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
    $uid = (int)($_POST['uid'] ?? 0);
    $user = $db->one('SELECT * FROM users WHERE id = ?', [$uid]);
    if ($user === null) {
        flash_set('err', 'No such caller.');
        admin_redirect('users.php');
    }
    $back = 'users.php' . (($_POST['back'] ?? '') !== '' ? '?' . (string)$_POST['back'] : '');
    switch ((string)($_POST['action'] ?? '')) {
        case 'approve':
            $db->q("UPDATE users SET status = 'active' WHERE id = ?", [$uid]);
            admin_log('Approved caller ' . $user['handle']);
            flash_set('ok', $user['handle'] . ' is aboard. They can log in now.');
            admin_redirect($back);

        case 'deny':
            $db->q("UPDATE users SET status = 'deleted' WHERE id = ?", [$uid]);
            admin_log('Denied application from ' . $user['handle']);
            flash_set('ok', 'Application from ' . $user['handle'] . ' denied.');
            admin_redirect($back);

        case 'delete':
            if ($uid === (int)$me['id']) {
                flash_set('err', 'Deleting yourself from your own console is a bit much.');
                admin_redirect($back);
            }
            $db->q("UPDATE users SET status = 'deleted' WHERE id = ?", [$uid]);
            admin_log('Soft-deleted caller ' . $user['handle']);
            flash_set('ok', $user['handle'] . ' marked deleted (soft; the row stays).');
            admin_redirect($back);

        case 'save':
            $level = max(0, min(255, (int)($_POST['level'] ?? $user['level'])));
            if ($uid === (int)$me['id'] && $level !== 255) {
                flash_set('err', 'You cannot demote the account you are logged in with.');
                admin_redirect('users.php?edit=' . $uid);
            }
            $flags = '';
            foreach (range('A', 'Z') as $f) {
                if (isset($_POST['flag_' . $f])) {
                    $flags .= $f;
                }
            }
            $status = (string)($_POST['status'] ?? $user['status']);
            if (!in_array($status, ['active', 'pending', 'locked', 'deleted'], true)) {
                $status = (string)$user['status'];
            }
            if ($uid === (int)$me['id'] && $status !== 'active') {
                flash_set('err', 'You cannot lock the account you are logged in with.');
                admin_redirect('users.php?edit=' . $uid);
            }
            // Empty means "follow the board's default theme" (1.1.0).
            $theme = (string)($_POST['theme'] ?? '');
            if ($theme !== '' && !preg_match('/^[a-z0-9_-]{1,32}$/', $theme)) {
                $theme = '';
            }
            $email = trim((string)($_POST['email'] ?? ''));
            if ($email !== '' && !\Atdt\Mailer::plausible($email)) {
                flash_set('err', 'That email does not look like a mailbox (or leave it blank). Nothing saved.');
                admin_redirect('users.php?edit=' . $uid);
            }
            // Last call (1.7.0, sysop request: imported histories should
            // show their real dates). Board-clock input, stored UTC like
            // every stamp. An untouched field is left alone so the stored
            // seconds survive the round trip; blank means never called.
            $lastRaw = trim((string)($_POST['last_login'] ?? ''));
            $lastShown = $user['last_login'] === null ? '' : admin_stamp((string)$user['last_login'], 'Y-m-d H:i');
            $lastTouched = $lastRaw !== $lastShown;
            $lastUtc = null;
            if ($lastTouched && $lastRaw !== '') {
                try {
                    $tz = new DateTimeZone((string)($settings->get('timezone', 'UTC') ?? 'UTC'));
                } catch (Throwable $e) {
                    $tz = new DateTimeZone('UTC');
                }
                $t = false;
                foreach (['!Y-m-d H:i', '!Y-m-d'] as $fmt) {
                    $t = DateTimeImmutable::createFromFormat($fmt, $lastRaw, $tz);
                    $errs = DateTimeImmutable::getLastErrors();
                    if ($t !== false && ($errs === false
                        || ((int)$errs['warning_count'] === 0 && (int)$errs['error_count'] === 0))) {
                        break;
                    }
                    $t = false;
                }
                if ($t === false) {
                    flash_set('err', 'Last call: YYYY-MM-DD HH:MM or YYYY-MM-DD on the board clock, or blank for never. Nothing saved.');
                    admin_redirect('users.php?edit=' . $uid);
                }
                $lastUtc = $t->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d\TH:i:s\Z');
            }
            $db->q(
                'UPDATE users SET level = ?, flags = ?, status = ?, location = ?, email = ?, note = ?, theme = ? WHERE id = ?',
                [
                    $level,
                    $flags,
                    $status,
                    mb_strimwidth(trim((string)($_POST['location'] ?? '')), 0, 40),
                    mb_strimwidth($email, 0, 120),
                    mb_strimwidth(trim((string)($_POST['note'] ?? '')), 0, 500),
                    $theme,
                    $uid,
                ]
            );
            if ($lastTouched) {
                $db->q('UPDATE users SET last_login = ? WHERE id = ?', [$lastUtc, $uid]);
            }
            $newPass = (string)($_POST['new_pass'] ?? '');
            if ($newPass !== '') {
                if (strlen($newPass) < 8) {
                    flash_set('err', 'Password not reset: 8 characters minimum. Other fields saved.');
                    admin_redirect('users.php?edit=' . $uid);
                }
                $db->q('UPDATE users SET pass_hash = ? WHERE id = ?', [password_hash($newPass, PASSWORD_DEFAULT), $uid]);
                atdt_log_event('security', 'console:' . $me['handle'], 'Password reset for ' . $user['handle']);
            }
            admin_log('Edited caller ' . $user['handle']);
            flash_set('ok', $user['handle'] . ' saved.');
            admin_redirect('users.php?edit=' . $uid);
    }
    admin_redirect('users.php');
}

// --- Listing ----------------------------------------------------------------
$q = trim((string)($_GET['q'] ?? ''));
$status = (string)($_GET['status'] ?? '');
$sort = (string)($_GET['sort'] ?? 'handle');
$dir = (string)($_GET['dir'] ?? 'asc') === 'desc' ? 'DESC' : 'ASC';
$sortCols = ['handle' => 'handle', 'level' => 'level', 'calls' => 'calls', 'posts' => 'posts', 'last' => 'last_login', 'joined' => 'created_at'];
$orderBy = ($sortCols[$sort] ?? 'handle') . ' ' . $dir;

$where = [];
$params = [];
if ($q !== '') {
    $where[] = '(handle LIKE ? OR location LIKE ?)';
    $params[] = '%' . $q . '%';
    $params[] = '%' . $q . '%';
}
if (in_array($status, ['pending', 'active', 'locked', 'deleted'], true)) {
    $where[] = 'status = ?';
    $params[] = $status;
}
$users = $db->all(
    'SELECT * FROM users' . ($where === [] ? '' : ' WHERE ' . implode(' AND ', $where)) .
    " ORDER BY CASE WHEN status = 'pending' THEN 0 ELSE 1 END, $orderBy LIMIT 200",
    $params
);
$editUser = isset($_GET['edit']) ? $db->one('SELECT * FROM users WHERE id = ?', [(int)$_GET['edit']]) : null;
$backQs = http_build_query(array_filter(['q' => $q, 'status' => $status, 'sort' => $sort !== 'handle' ? $sort : '', 'dir' => $dir === 'DESC' ? 'desc' : '']));
$themes = array_merge(
    array_map('basename', glob(ATDT_ROOT . '/themes/*', GLOB_ONLYDIR) ?: []),
    array_map('basename', glob(ATDT_ROOT . '/data/themes/*', GLOB_ONLYDIR) ?: [])
);
$themes = array_values(array_unique($themes));

/** Sort-header link that flips direction on the active column. */
function sort_link(string $key, string $label): string
{
    $cur = (string)($_GET['sort'] ?? 'handle');
    $dir = (string)($_GET['dir'] ?? 'asc');
    $next = ($cur === $key && $dir !== 'desc') ? 'desc' : 'asc';
    $qs = http_build_query(array_merge($_GET, ['sort' => $key, 'dir' => $next]));
    $mark = $cur === $key ? ($dir === 'desc' ? ' ↓' : ' ↑') : '';
    return '<a href="users.php?' . h($qs) . '">' . h($label) . $mark . '</a>';
}

admin_page_start('Users', 'users');
?>

<form method="get" class="row card">
  <input type="text" name="q" placeholder="search handle or location…" value="<?= h($q) ?>">
  <select name="status">
    <option value="">any status</option>
    <?php foreach (['pending', 'active', 'locked', 'deleted'] as $s): ?>
    <option value="<?= $s ?>"<?= $status === $s ? ' selected' : '' ?>><?= $s ?></option>
    <?php endforeach; ?>
  </select>
  <button class="btn small">Filter</button>
  <?php if ($q !== '' || $status !== ''): ?><a href="users.php">clear</a><?php endif; ?>
</form>

<?php if ($editUser !== null): ?>
<div class="card drawer">
  <h2>Edit: <?= h((string)$editUser['handle']) ?>
    <span class="hint">joined <?= h(admin_stamp((string)$editUser['created_at'], 'Y-m-d')) ?>,
    <?= (int)$editUser['calls'] ?> calls, <?= (int)$editUser['posts'] ?> posts<?=
        (string)($editUser['last_ip'] ?? '') !== ''
            ? ' &middot; last IP <code>' . h((string)$editUser['last_ip']) . '</code> (ban it under Settings &gt; Banned addresses)'
            : '' ?></span></h2>
  <form method="post">
    <?= csrf_field() ?>
    <input type="hidden" name="action" value="save">
    <input type="hidden" name="uid" value="<?= (int)$editUser['id'] ?>">
    <div class="grid2">
      <div>
        <label>Level <span class="hint">(255 = SysOp)</span>
          <input type="text" name="level" size="6" value="<?= (int)$editUser['level'] ?>"></label>
        <label>Status
          <select name="status">
            <?php foreach (['active', 'pending', 'locked', 'deleted'] as $s): ?>
            <option value="<?= $s ?>"<?= (string)$editUser['status'] === $s ? ' selected' : '' ?>><?= $s ?></option>
            <?php endforeach; ?>
          </select></label>
        <label>Location
          <input type="text" name="location" value="<?= h((string)$editUser['location']) ?>"></label>
        <label>Email <span class="hint">(password resets go here; only this console sees it)</span>
          <input type="text" name="email" maxlength="120" value="<?= h((string)($editUser['email'] ?? '')) ?>"></label>
        <label>Theme
          <select name="theme">
            <option value=""<?= (string)$editUser['theme'] === '' ? ' selected' : '' ?>>(board default)</option>
            <?php foreach ($themes as $t): ?>
            <option value="<?= h($t) ?>"<?= (string)$editUser['theme'] === $t ? ' selected' : '' ?>><?= h($t) ?></option>
            <?php endforeach; ?>
          </select></label>
        <label>Last call <span class="hint">(board clock, <code>YYYY-MM-DD HH:MM</code> or just the date; blank = never. For imported histories)</span>
          <input type="text" name="last_login" size="18" value="<?= h($editUser['last_login'] === null ? '' : admin_stamp((string)$editUser['last_login'], 'Y-m-d H:i')) ?>"></label>
      </div>
      <div>
        <label>Access flags <span class="hint">(ACS <code>fX</code> checks these)</span></label>
        <div class="flags">
          <?php foreach (range('A', 'Z') as $f): ?>
          <label><input type="checkbox" name="flag_<?= $f ?>"<?= str_contains((string)$editUser['flags'], $f) ? ' checked' : '' ?>> <?= $f ?></label>
          <?php endforeach; ?>
        </div>
        <label>SysOp note <span class="hint">(only the console sees this)</span>
          <textarea name="note" rows="3" style="width:100%"><?= h((string)$editUser['note']) ?></textarea></label>
        <label>Reset password <span class="hint">(blank = unchanged, 8+ chars)</span>
          <input type="text" name="new_pass" autocomplete="off"></label>
      </div>
    </div>
    <button class="btn">Save caller</button>
    <a class="btn secondary" href="users.php<?= $backQs !== '' ? '?' . h($backQs) : '' ?>">Close</a>
  </form>
  <?php if ((int)$editUser['id'] !== (int)$me['id']): ?>
  <form method="post" onsubmit="return confirm('Soft-delete <?= h((string)$editUser['handle']) ?>? They can no longer log in; the row stays.');" class="inline">
    <?= csrf_field() ?>
    <input type="hidden" name="action" value="delete">
    <input type="hidden" name="uid" value="<?= (int)$editUser['id'] ?>">
    <button class="btn danger small">Delete (soft)</button>
  </form>
  <?php endif; ?>
</div>
<?php endif; ?>

<div class="card">
  <table class="list">
    <tr>
      <th><?= sort_link('handle', 'Handle') ?></th>
      <th><?= sort_link('level', 'Lvl') ?></th>
      <th>Status</th>
      <th>Location</th>
      <th><?= sort_link('calls', 'Calls') ?></th>
      <th><?= sort_link('posts', 'Posts') ?></th>
      <th class="nowrap"><?= sort_link('last', 'Last on') ?></th>
      <th></th>
    </tr>
    <?php if ($users === []): ?><tr><td colspan="8" class="hint">Nobody matches.</td></tr><?php endif; ?>
    <?php foreach ($users as $u): ?>
    <tr<?= (string)$u['status'] === 'pending' ? ' class="pending"' : '' ?>>
      <td><?= h((string)$u['handle']) ?></td>
      <td><?= (int)$u['level'] ?></td>
      <td><?= h((string)$u['status']) ?></td>
      <td><?= h((string)$u['location']) ?></td>
      <td><?= (int)$u['calls'] ?></td>
      <td><?= (int)$u['posts'] ?></td>
      <td class="nowrap"><?= h($u['last_login'] === null ? 'never' : admin_stamp((string)$u['last_login'], 'Y-m-d')) ?></td>
      <td class="nowrap">
        <?php if ((string)$u['status'] === 'pending'): ?>
        <form method="post" class="inline"><?= csrf_field() ?>
          <input type="hidden" name="action" value="approve"><input type="hidden" name="uid" value="<?= (int)$u['id'] ?>">
          <input type="hidden" name="back" value="<?= h($backQs) ?>">
          <button class="btn small">Approve</button></form>
        <form method="post" class="inline"><?= csrf_field() ?>
          <input type="hidden" name="action" value="deny"><input type="hidden" name="uid" value="<?= (int)$u['id'] ?>">
          <input type="hidden" name="back" value="<?= h($backQs) ?>">
          <button class="btn small danger">Deny</button></form>
        <?php endif; ?>
        <a href="users.php?<?= h($backQs !== '' ? $backQs . '&' : '') ?>edit=<?= (int)$u['id'] ?>">edit</a>
      </td>
    </tr>
    <?php endforeach; ?>
  </table>
  <?php if (count($users) === 200): ?><p class="hint">Showing the first 200. Narrow the search.</p><?php endif; ?>
</div>

<?php admin_page_end(); ?>
