<?php

require __DIR__ . '/../bootstrap.php';

use KeyLair\Auth;
use KeyLair\AuditLog;
use KeyLair\Csrf;
use KeyLair\Database;
use KeyLair\DemoMask;
use KeyLair\Helpers;
use KeyLair\I18n;

// Same access level as Claim Links itself -- this is just a different view
// over the same users table, not a separate entity with its own edit
// rights ("editing" a user only ever happens by editing the claim links
// that reference them, or by anonymizing the row below). This is the
// claim-link creator directory, a completely separate table from the
// admin-panel login accounts on System Users (still the `admins` table)
// -- never merge the two.
$admin = Auth::requireLogin();
$isDemo = $admin['role'] === 'demo';
$pdo = Database::connection();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    Auth::requireEditAccess();
    Csrf::requireValid();
    $action = $_POST['form_action'] ?? '';
    $id = (int) ($_POST['id'] ?? 0);

    if ($action === 'anonymize') {
        // GDPR erasure: the users row itself stays (claim_links.user_id is
        // NOT NULL and still needs a valid id to point at, and the link/
        // redemption history is legitimate business record-keeping), but
        // the personal data on it -- name, email -- is wiped. A user
        // anonymized this way still shows up here and on every page that
        // references them, just as "User #<id>" instead of a name.
        $pdo->prepare('UPDATE users SET name = NULL, email = NULL WHERE id = :id')->execute(['id' => $id]);
        AuditLog::log($admin['id'], 'user_anonymized', (string) $id);
        Helpers::redirect('users.php');
    } elseif ($action === 'bulk_update') {
        // Every row's Name/Email/Creator group is editable directly in the
        // table (see the form below) rather than one-at-a-time on a
        // separate edit page, so several users can be corrected in a
        // single save. Creator group isn't a users column -- it lives on
        // claim_links -- so picking one here applies it to every one of
        // this user's claim links at once, same as Name/Email are treated
        // as one value per user even though they only ever get read back
        // via the most recent link elsewhere on this page.
        $names = $_POST['name'] ?? [];
        $emails = $_POST['email'] ?? [];
        $groups = $_POST['creator_group'] ?? [];
        $userStmt = $pdo->prepare('UPDATE users SET name = :name, email = :email WHERE id = :id');
        $groupStmt = $pdo->prepare('UPDATE claim_links SET creator_group_id = :group_id WHERE user_id = :id');
        foreach ($names as $uid => $nameRaw) {
            $uid = (int) $uid;
            $name = trim((string) $nameRaw);
            $email = trim((string) ($emails[$uid] ?? ''));
            $userStmt->execute([
                'name' => $name !== '' ? $name : null,
                'email' => $email !== '' ? $email : null,
                'id' => $uid,
            ]);
            if (isset($groups[$uid])) {
                $groupRaw = trim((string) $groups[$uid]);
                $groupStmt->execute(['group_id' => $groupRaw !== '' ? (int) $groupRaw : null, 'id' => $uid]);
            }
        }
        AuditLog::log($admin['id'], 'users_bulk_updated', 'count=' . count($names));
        Helpers::redirect('users.php?saved=1');
    }
    Helpers::redirect('users.php');
}

$users = $pdo->query(
    "SELECT u.id, u.name, u.email,
        COUNT(DISTINCT cl.id) AS link_count,
        COALESCE(SUM(clp.claims_used), 0) AS claims_used_total,
        COALESCE(SUM(clp.max_claims), 0) AS claims_max_total,
        MAX(cl.created_at) AS last_link_created,
        (SELECT cl2.creator_group_id FROM claim_links cl2
         WHERE cl2.user_id = u.id AND cl2.creator_group_id IS NOT NULL
         ORDER BY cl2.created_at DESC LIMIT 1) AS creator_group_id,
        (SELECT cg.name FROM claim_links cl2
         JOIN creator_groups cg ON cg.id = cl2.creator_group_id
         WHERE cl2.user_id = u.id AND cl2.creator_group_id IS NOT NULL
         ORDER BY cl2.created_at DESC LIMIT 1) AS creator_group_name
     FROM users u
     LEFT JOIN claim_links cl ON cl.user_id = u.id
     LEFT JOIN claim_link_products clp ON clp.claim_link_id = cl.id
     GROUP BY u.id
     ORDER BY u.name ASC, u.id ASC"
)->fetchAll();
$creatorGroups = $pdo->query('SELECT id, name FROM creator_groups ORDER BY name ASC')->fetchAll();

$pageTitle = I18n::t('users.page_title');
$activeNav = 'users';
$extraAdminCss = 'admin-product-editor';
require __DIR__ . '/includes/layout_header.php';
?>
<div class="topbar">
  <h1><?= Helpers::e(I18n::t('users.page_title')) ?></h1>
  <?php if (Auth::canEdit()): ?>
  <a href="user_form.php" class="campaign-select" style="text-decoration:none; color:var(--signal); border-color:var(--signal-dim);"><?= Helpers::e(I18n::t('users.new_user')) ?></a>
  <?php elseif ($isDemo): ?>
  <span class="campaign-select" style="text-decoration:none; color:var(--text-faint); border-color:var(--signal-dim); cursor:not-allowed;"><?= Helpers::e(I18n::t('users.new_user')) ?></span>
  <?php endif; ?>
</div>

<?php if (!empty($_GET['saved'])): ?>
  <div class="panel" style="border-color:var(--signal-dim); margin-bottom:16px;">
    <p style="color:var(--signal); font-size:13px; margin:0;"><?= Helpers::e(I18n::t('users.saved_message')) ?></p>
  </div>
<?php endif; ?>

<div class="panel">
  <?php if (empty($users)): ?>
    <p style="color:var(--text-faint); font-size:13px;"><?= sprintf(Helpers::e(I18n::t('users.empty_state')), '<a href="claim_links.php" style="color:var(--signal);">' . Helpers::e(I18n::t('nav.claim_links')) . '</a>') ?></p>
  <?php else: ?>
  <?php $kvTableInputStyle = 'width:100%; min-width:120px; box-sizing:border-box; background:var(--bg-void); border:1px solid var(--line); color:var(--text-primary); border-radius:6px; padding:6px 8px; font-size:12.5px;'; ?>
  <div style="margin-bottom:12px;">
    <input type="text" id="users-search" oninput="kvUsersFilter();" placeholder="<?= Helpers::e(I18n::t('users.search_placeholder')) ?>" style="<?= $kvTableInputStyle ?> max-width:320px;">
  </div>
  <?php if (Auth::canEdit()): ?>
  <form method="post" id="users-bulk-form">
    <?= Csrf::field() ?>
    <input type="hidden" name="form_action" value="bulk_update">
  <?php endif; ?>
  <table id="users-table">
    <thead><tr><th><?= Helpers::e(I18n::t('users.col_creator')) ?></th><th><?= Helpers::e(I18n::t('users.col_email')) ?></th><th><?= Helpers::e(I18n::t('users.col_creator_group')) ?></th><th><?= Helpers::e(I18n::t('users.col_claim_links')) ?></th><th><?= Helpers::e(I18n::t('users.col_redemptions')) ?></th><th><?= Helpers::e(I18n::t('users.col_last_link_created')) ?></th><th></th></tr></thead>
    <tbody>
      <?php foreach ($users as $u):
        $displayName = $u['name'] !== null ? DemoMask::name($u['name']) : sprintf(I18n::t('users.user_number'), (int) $u['id']);
        $hasPersonalData = $u['name'] !== null || $u['email'] !== null;
      ?>
      <tr data-name="<?= Helpers::e(mb_strtolower($displayName)) ?>" data-email="<?= Helpers::e(mb_strtolower($u['email'] ?? '')) ?>">
        <?php if (Auth::canEdit()): ?>
        <td data-label="<?= Helpers::e(I18n::t('users.col_creator')) ?>"><input type="text" name="name[<?= (int) $u['id'] ?>]" value="<?= Helpers::e($u['name'] ?? '') ?>" placeholder="<?= Helpers::e(sprintf(I18n::t('users.user_number'), (int) $u['id'])) ?>" style="<?= $kvTableInputStyle ?>"></td>
        <td data-label="<?= Helpers::e(I18n::t('users.col_email')) ?>"><input type="email" name="email[<?= (int) $u['id'] ?>]" value="<?= Helpers::e($u['email'] ?? '') ?>" placeholder="—" style="<?= $kvTableInputStyle ?>"></td>
        <td data-label="<?= Helpers::e(I18n::t('users.col_creator_group')) ?>">
          <select name="creator_group[<?= (int) $u['id'] ?>]" style="<?= $kvTableInputStyle ?>">
            <option value=""><?= Helpers::e(I18n::t('users.none_option')) ?></option>
            <?php foreach ($creatorGroups as $g): ?>
              <option value="<?= (int) $g['id'] ?>" <?= ((int) ($u['creator_group_id'] ?? 0) === (int) $g['id']) ? 'selected' : '' ?>><?= Helpers::e($g['name']) ?></option>
            <?php endforeach; ?>
          </select>
        </td>
        <?php else: ?>
        <td class="creator" data-label="<?= Helpers::e(I18n::t('users.col_creator')) ?>"><?= Helpers::e($displayName) ?></td>
        <td data-label="<?= Helpers::e(I18n::t('users.col_email')) ?>"><?= $u['email'] !== null ? Helpers::e($u['email']) : '<span style="color:var(--text-faint);">—</span>' ?></td>
        <td data-label="<?= Helpers::e(I18n::t('users.col_creator_group')) ?>"><?= $u['creator_group_name'] !== null ? Helpers::e($u['creator_group_name']) : '<span style="color:var(--text-faint);">—</span>' ?></td>
        <?php endif; ?>
        <td class="time-cell" data-label="<?= Helpers::e(I18n::t('users.col_claim_links')) ?>">
          <?php if ((int) $u['link_count'] > 0): ?>
            <a href="claim_links.php?user_id=<?= (int) $u['id'] ?>" target="_blank" rel="noopener" style="color:var(--signal); text-decoration:none;"><?= (int) $u['link_count'] ?></a>
          <?php else: ?>
            0
          <?php endif; ?>
        </td>
        <td class="time-cell" data-label="<?= Helpers::e(I18n::t('users.col_redemptions')) ?>"><?= (int) $u['claims_used_total'] ?> / <?= (int) $u['claims_max_total'] ?></td>
        <td class="time-cell" data-label="<?= Helpers::e(I18n::t('users.col_last_link_created')) ?>"><?= $u['last_link_created'] !== null ? Helpers::e($u['last_link_created']) : '<span style="color:var(--text-faint);">—</span>' ?></td>
        <td style="white-space:nowrap;">
          <?php if (Auth::canEdit()): ?>
          <button type="button" onclick="kvSaveUserRow(<?= (int) $u['id'] ?>, this);" style="background:none; border:none; color:var(--signal); font-size:12px; cursor:pointer; padding:0; margin-right:10px;"><?= Helpers::e(I18n::t('users.save')) ?></button>
          <?php if ($hasPersonalData): ?>
          <button type="button" onclick="kvAnonymizeUser(<?= (int) $u['id'] ?>);" style="background:none; border:none; color:var(--danger); font-size:12px; cursor:pointer; padding:0;"><?= Helpers::e(I18n::t('users.delete_personal_data')) ?></button>
          <?php endif; ?>
          <?php elseif ($isDemo): ?>
          <span style="color:var(--text-faint); font-size:12px; text-decoration:none; margin-right:10px; cursor:not-allowed;"><?= Helpers::e(I18n::t('users.save')) ?></span>
          <span style="color:var(--text-faint); font-size:12px; cursor:not-allowed;"><?= Helpers::e(I18n::t('users.delete_personal_data')) ?></span>
          <?php endif; ?>
        </td>
      </tr>
      <?php endforeach; ?>
    </tbody>
  </table>
  <div id="users-pagination" style="display:flex; justify-content:center; align-items:center; gap:14px; flex-wrap:wrap; margin-top:16px;">
    <button type="button" class="btn btn-ghost" id="users-prev-btn" onclick="kvUsersChangePage(-1);" style="padding:7px 14px; font-size:12.5px;"><?= Helpers::e(I18n::t('users.prev')) ?></button>
    <span id="users-page-info" style="color:var(--text-muted); font-size:13px; white-space:nowrap;"></span>
    <button type="button" class="btn btn-ghost" id="users-next-btn" onclick="kvUsersChangePage(1);" style="padding:7px 14px; font-size:12.5px;"><?= Helpers::e(I18n::t('users.next')) ?></button>
    <select id="users-page-size" onchange="kvUsersChangePageSize();" style="background:var(--bg-panel); border:1px solid var(--line); color:var(--text-primary); font-size:12.5px; padding:7px 12px; border-radius:8px;">
      <option value="10"><?= Helpers::e(sprintf(I18n::t('users.page_size_option'), 10)) ?></option>
      <option value="20"><?= Helpers::e(sprintf(I18n::t('users.page_size_option'), 20)) ?></option>
      <option value="50" selected><?= Helpers::e(sprintf(I18n::t('users.page_size_option'), 50)) ?></option>
      <option value="100"><?= Helpers::e(sprintf(I18n::t('users.page_size_option'), 100)) ?></option>
      <option value="200"><?= Helpers::e(sprintf(I18n::t('users.page_size_option'), 200)) ?></option>
      <option value="all"><?= Helpers::e(I18n::t('users.show_all')) ?></option>
    </select>
  </div>
  <?php if (count($users) > 20): ?>
  <?php // Same fixed jump-to-first/last-row buttons as the Keys/Codes list
  // -- once there are thousands of users and "Show all" is selected, this
  // is the only fast way back to either end after scrolling into the
  // middle of the table. ?>
  <div style="position:fixed; right:20px; bottom:20px; display:flex; flex-direction:column; gap:8px; z-index:50;">
    <button type="button" class="btn btn-ghost" onclick="kvScrollToRow('first');" title="<?= Helpers::e(I18n::t('users.jump_first')) ?>" style="width:40px; height:40px; padding:0; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:16px; background:var(--bg-panel); box-shadow:0 2px 8px rgba(0,0,0,0.3);">↑</button>
    <button type="button" class="btn btn-ghost" onclick="kvScrollToRow('last');" title="<?= Helpers::e(I18n::t('users.jump_last')) ?>" style="width:40px; height:40px; padding:0; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:16px; background:var(--bg-panel); box-shadow:0 2px 8px rgba(0,0,0,0.3);">↓</button>
  </div>
  <?php endif; ?>
  <?php if (Auth::canEdit()): ?>
  <div style="display:flex; justify-content:flex-end; margin-top:10px;">
    <button type="submit" class="btn btn-primary"><?= Helpers::e(I18n::t('users.save_changes')) ?></button>
  </div>
  </form>
  <?php endif; ?>
  <?php endif; ?>
</div>

<?php if (Auth::canEdit()): ?>
<!-- Two more standalone forms, outside the bulk-edit form above since
     forms can't nest inside each other: one for a single row's "Save"
     (reuses the same bulk_update handler with just that row's data),
     one for "Delete personal data". Each JS function fills in which row
     before submitting. -->
<form method="post" id="single-save-form" style="display:none;">
  <?= Csrf::field() ?>
  <input type="hidden" name="form_action" value="bulk_update">
  <input type="hidden" id="single-save-name">
  <input type="hidden" id="single-save-email">
  <input type="hidden" id="single-save-group">
</form>
<form method="post" id="anonymize-form" style="display:none;">
  <?= Csrf::field() ?>
  <input type="hidden" name="form_action" value="anonymize">
  <input type="hidden" name="id" id="anonymize-form-id">
</form>
<script>
function kvSaveUserRow(id, btn) {
  var row = btn.closest('tr');
  var nameField = document.getElementById('single-save-name');
  var emailField = document.getElementById('single-save-email');
  var groupField = document.getElementById('single-save-group');
  nameField.name = 'name[' + id + ']';
  nameField.value = row.querySelector('input[name="name[' + id + ']"]').value;
  emailField.name = 'email[' + id + ']';
  emailField.value = row.querySelector('input[name="email[' + id + ']"]').value;
  groupField.name = 'creator_group[' + id + ']';
  groupField.value = row.querySelector('select[name="creator_group[' + id + ']"]').value;
  document.getElementById('single-save-form').submit();
}
function kvAnonymizeUser(id) {
  if (!confirm(<?= json_encode(I18n::t('users.anonymize_confirm_js'), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>)) return;
  document.getElementById('anonymize-form-id').value = id;
  document.getElementById('anonymize-form').submit();
}
</script>
<?php endif; ?>

<?php if (!empty($users)): ?>
<script>
// Jumps to whichever row is currently first/last among the ones
// pagination leaves visible -- not just the first/last in the DOM, which
// could easily be an off-page row. Same behavior as Keys/Codes' own
// jump buttons. Runs regardless of edit access, unlike the save/
// anonymize functions above -- pagination applies to everyone browsing
// this list.
function kvScrollToRow(which) {
  const rows = [...document.querySelectorAll('#users-table tbody tr')].filter(tr => tr.style.display !== 'none');
  if (rows.length === 0) return;
  const target = which === 'first' ? rows[0] : rows[rows.length - 1];
  target.scrollIntoView({ behavior: 'smooth', block: 'center' });
}

let kvUsersPage = 1;
let kvUsersPageSize = 50;
const kvUsersPageOfTemplate = <?= json_encode(I18n::t('users.pagination_page_of'), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;

// Marks which rows match the search box (data attribute, not display,
// since pagination decides display) -- same split as Keys/Codes' own
// filter + pagination combo.
function kvUsersFilter() {
  const q = document.getElementById('users-search').value.trim().toLowerCase();
  document.querySelectorAll('#users-table tbody tr').forEach(tr => {
    const matches = !q || (tr.dataset.name || '').includes(q) || (tr.dataset.email || '').includes(q);
    tr.dataset.matchesFilter = matches ? '1' : '0';
  });
  kvUsersPage = 1;
  kvRenderUsersPage();
}

// Hides rows outside the current page (or not matching the search box)
// via display:none rather than removing them from the DOM -- a hidden
// row's inputs still submit normally with the bulk-edit form
// (display:none has no effect on form serialization), so filtering and
// paging never lose an off-page/filtered-out edit.
function kvRenderUsersPage() {
  const rows = [...document.querySelectorAll('#users-table tbody tr')];
  const matching = rows.filter(tr => tr.dataset.matchesFilter !== '0');
  const total = matching.length;
  const pageSize = kvUsersPageSize === 'all' ? Math.max(total, 1) : kvUsersPageSize;
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  if (kvUsersPage > totalPages) kvUsersPage = totalPages;
  const start = (kvUsersPage - 1) * pageSize;
  const end = kvUsersPageSize === 'all' ? total : start + pageSize;
  const shown = new Set(matching.slice(start, end));
  rows.forEach(tr => { tr.style.display = shown.has(tr) ? '' : 'none'; });

  document.getElementById('users-page-info').textContent = kvUsersPageOfTemplate.replace('%d', kvUsersPage).replace('%d', totalPages);
  document.getElementById('users-prev-btn').disabled = kvUsersPage <= 1;
  document.getElementById('users-next-btn').disabled = kvUsersPage >= totalPages;
}

function kvUsersChangePage(delta) {
  kvUsersPage += delta;
  kvRenderUsersPage();
}

function kvUsersChangePageSize() {
  const val = document.getElementById('users-page-size').value;
  kvUsersPageSize = val === 'all' ? 'all' : parseInt(val, 10);
  kvUsersPage = 1;
  kvRenderUsersPage();
}

kvRenderUsersPage();
</script>
<?php endif; ?>

<?php require __DIR__ . '/includes/layout_footer.php'; ?>
