<?php

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

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

$viewer = Auth::requireAdminTierView();
$isDemo = $viewer['role'] === 'demo';
$isMasterAdmin = $viewer['role'] === 'master_admin';
// A plain 'admin' can only manage the tiers below 'admin' itself —
// never another admin or a master admin, even by guessing an id in the POST.
// Demo can manage no one -- it only ever previews this page.
$manageableRoles = $isDemo
    ? []
    : ($isMasterAdmin
        ? ['master_admin', 'admin', 'content_manager', 'reviewer', 'demo']
        : ['content_manager', 'reviewer', 'demo']);
// Which roles' accounts to list -- demo previews the full picture a master
// admin would see (with every value masked below), not just the slice it
// could theoretically manage, since it has nothing it can manage anyway.
$visibleRoles = $isDemo ? ['master_admin', 'admin', 'content_manager', 'reviewer', 'demo'] : $manageableRoles;
$pdo = Database::connection();

$flash = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if ($isDemo) {
        http_response_code(403);
        die(I18n::t('system_users.error_demo_readonly'));
    }
    Csrf::requireValid();
    $action = $_POST['form_action'] ?? '';

    if ($action === 'create') {
        $username = trim($_POST['username'] ?? '');
        $email = trim($_POST['email'] ?? '');
        $password = $_POST['password'] ?? '';
        $role = $_POST['role'] ?? '';

        // Demo accounts aren't real credentials -- they're a stable, shareable
        // login for a prospective buyer to click around with, never editable
        // by the account itself (see Auth::canEditOwnAccount()), so the usual
        // 10-char minimum is just friction here rather than a real security
        // measure. Still can't be blank.
        $minPasswordLength = $role === 'demo' ? 1 : 10;

        // Same reasoning for email: a demo login has no one to actually
        // receive mail at it (password reset is already blocked for demo
        // accounts, see PasswordReset::request()), so requiring one is just
        // friction. Left blank, synthesize a placeholder -- a random token
        // rather than the username itself, since the username has no
        // charset restriction of its own and might not form a valid email
        // local-part -- rather than leaving the NOT NULL UNIQUE email
        // column empty (which would collide the moment a second
        // blank-email account was created). Never actually mailed. If an
        // email was typed anyway, it's used as given.
        if ($role === 'demo' && $email === '') {
            $email = 'demo-' . bin2hex(random_bytes(6)) . '@demo.local';
        }

        if (!in_array($role, $manageableRoles, true)) {
            $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.invalid_role')];
        } elseif (strlen($username) < 3 || !filter_var($email, FILTER_VALIDATE_EMAIL) || strlen($password) < $minPasswordLength) {
            $message = $role === 'demo'
                ? I18n::t('system_users.flash.invalid_demo_signup')
                : I18n::t('system_users.flash.invalid_signup');
            $flash = ['type' => 'error', 'message' => $message];
        } else {
            $check = $pdo->prepare('SELECT id FROM admin_users WHERE username = :u OR email = :e');
            $check->execute(['u' => $username, 'e' => $email]);
            if ($check->fetch()) {
                $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.username_email_taken')];
            } else {
                $pdo->prepare('INSERT INTO admin_users (username, email, password_hash, role, is_active, created_at) VALUES (:u,:e,:p,:r,1,NOW())')
                    ->execute(['u' => $username, 'e' => $email, 'p' => password_hash($password, PASSWORD_DEFAULT), 'r' => $role]);
                AuditLog::log($viewer['id'], 'admin_created', "{$username} ({$role})");
                $flash = ['type' => 'success', 'message' => I18n::t('system_users.flash.account_created')];
            }
        }
    } elseif (in_array($action, ['save_account', 'delete', 'reset_password'], true)) {
        $targetId = (int) ($_POST['id'] ?? 0);

        if ($targetId === (int) $viewer['id']) {
            $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.cannot_self_modify')];
        } else {
            $stmt = $pdo->prepare('SELECT * FROM admin_users WHERE id = :id');
            $stmt->execute(['id' => $targetId]);
            $target = $stmt->fetch();

            if (!$target || !in_array($target['role'], $manageableRoles, true)) {
                $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.account_not_found')];
            } elseif ($action === 'delete') {
                $pdo->prepare('DELETE FROM admin_users WHERE id = :id')->execute(['id' => $targetId]);
                AuditLog::log($viewer['id'], 'admin_deleted', $target['username']);
                $flash = ['type' => 'success', 'message' => I18n::t('system_users.flash.account_deleted')];
            } elseif ($action === 'reset_password') {
                if ($target['role'] === 'demo') {
                    // A demo account's email is a random @demo.local
                    // placeholder nobody actually reads (see
                    // PasswordReset::request()) -- a reset link would go
                    // nowhere, so hand the admin a fresh password directly,
                    // same as the old manual-reset flow.
                    $newPassword = bin2hex(random_bytes(6));
                    $pdo->prepare('UPDATE admin_users SET password_hash = :p WHERE id = :id')
                        ->execute(['p' => password_hash($newPassword, PASSWORD_DEFAULT), 'id' => $targetId]);
                    AuditLog::log($viewer['id'], 'admin_password_reset', $target['username']);
                    $flash = ['type' => 'success', 'message' => sprintf(I18n::t('system_users.flash.password_reset_manual'), $target['username'], $newPassword)];
                } else {
                    $sent = PasswordReset::requestForAccountId($targetId);
                    $flash = $sent
                        ? ['type' => 'success', 'message' => sprintf(I18n::t('system_users.flash.password_reset_sent'), $target['email'])]
                        : ['type' => 'error', 'message' => I18n::t('system_users.flash.password_reset_failed')];
                }
            } elseif ($action === 'save_account') {
                $newUsername = trim($_POST['new_username'] ?? '');
                $newEmail = trim($_POST['new_email'] ?? '');
                $newRole = $_POST['new_role'] ?? $target['role'];
                $newActive = ($_POST['account_status'] ?? ($target['is_active'] ? 'active' : 'disabled')) === 'active';
                $newTwoFactorState = $_POST['two_factor_state'] ?? null;

                if (strlen($newUsername) < 3) {
                    $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.username_too_short')];
                } elseif (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
                    $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.invalid_email')];
                } elseif (!in_array($newRole, $manageableRoles, true)) {
                    // Same set both ways: a viewer can only hand out a role it
                    // could itself have created/deleted an account as (an
                    // admin promoting someone to admin or master_admin, or
                    // reaching into a role it can't already manage, would be
                    // a privilege escalation this check exists to block).
                    $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.role_not_permitted')];
                } else {
                    $check = $pdo->prepare('SELECT id FROM admin_users WHERE (username = :u OR email = :e) AND id != :id');
                    $check->execute(['u' => $newUsername, 'e' => $newEmail, 'id' => $targetId]);
                    if ($check->fetch()) {
                        $flash = ['type' => 'error', 'message' => I18n::t('system_users.flash.username_email_taken')];
                    } else {
                        $sql = 'UPDATE admin_users SET username = :u, email = :e, role = :r, is_active = :a';
                        $params = ['u' => $newUsername, 'e' => $newEmail, 'r' => $newRole, 'a' => $newActive ? 1 : 0, 'id' => $targetId];
                        // Only ever touched when the account actually has a
                        // method enrolled -- the field isn't rendered (and
                        // this is ignored) otherwise, so there's nothing to
                        // suspend or restore.
                        if ($target['two_factor_method'] !== 'none' && in_array($newTwoFactorState, ['active', 'disabled'], true)) {
                            $sql .= ', two_factor_disabled_at = ' . ($newTwoFactorState === 'disabled' ? 'NOW()' : 'NULL');
                        }
                        $pdo->prepare($sql . ' WHERE id = :id')->execute($params);

                        $changes = [];
                        if ($newUsername !== $target['username'] || $newEmail !== $target['email']) {
                            $changes[] = 'identity';
                        }
                        if ($newRole !== $target['role']) {
                            $changes[] = "role: {$target['role']} -> {$newRole}";
                        }
                        if ($newActive !== (bool) $target['is_active']) {
                            $changes[] = $newActive ? 'reactivated' : 'disabled';
                        }
                        if ($target['two_factor_method'] !== 'none' && in_array($newTwoFactorState, ['active', 'disabled'], true)) {
                            $wasSuspended = !empty($target['two_factor_disabled_at']);
                            if (($newTwoFactorState === 'disabled') !== $wasSuspended) {
                                $changes[] = $newTwoFactorState === 'disabled' ? '2FA suspended' : '2FA restored';
                            }
                        }
                        AuditLog::log($viewer['id'], 'admin_account_updated', $newUsername . ($changes ? ' (' . implode(', ', $changes) . ')' : ''));
                        $flash = ['type' => 'success', 'message' => sprintf(I18n::t('system_users.flash.account_updated'), $newUsername)];
                    }
                }
            }
        }
    }
}

$placeholders = implode(',', array_fill(0, count($visibleRoles), '?'));
$admins = $pdo->prepare("SELECT * FROM admin_users WHERE role IN ({$placeholders}) OR id = ? ORDER BY FIELD(role,'master_admin','admin','content_manager','reviewer','demo'), username ASC");
$admins->execute([...$visibleRoles, $viewer['id']]);
$admins = $admins->fetchAll();

$pageTitle = I18n::t('system_users.page_title');
$activeNav = 'system_users';
$extraAdminCss = 'admin-product-editor';
require __DIR__ . '/includes/layout_header.php';
?>
<div class="topbar"><h1><?= Helpers::e(I18n::t('system_users.page_title')) ?></h1></div>

<div class="panel" style="margin-bottom:16px;">
  <p class="panel-title"><?= Helpers::e(I18n::t('system_users.roles_panel_title')) ?></p>
  <table>
    <thead><tr><th><?= Helpers::e(I18n::t('system_users.col_role')) ?></th><th><?= Helpers::e(I18n::t('system_users.col_permissions')) ?></th></tr></thead>
    <tbody>
      <tr>
        <td class="creator" data-label="<?= Helpers::e(I18n::t('system_users.col_role')) ?>"><?= Helpers::e(Auth::roleLabel('master_admin')) ?></td>
        <td class="time-cell role-desc" data-label="<?= Helpers::e(I18n::t('system_users.col_permissions')) ?>"><?= Helpers::e(I18n::t('system_users.role_desc_master_admin')) ?></td>
      </tr>
      <tr>
        <td class="creator" data-label="<?= Helpers::e(I18n::t('system_users.col_role')) ?>"><?= Helpers::e(Auth::roleLabel('admin')) ?></td>
        <td class="time-cell role-desc" data-label="<?= Helpers::e(I18n::t('system_users.col_permissions')) ?>"><?= Helpers::e(I18n::t('system_users.role_desc_admin')) ?></td>
      </tr>
      <tr>
        <td class="creator" data-label="<?= Helpers::e(I18n::t('system_users.col_role')) ?>"><?= Helpers::e(Auth::roleLabel('content_manager')) ?></td>
        <td class="time-cell role-desc" data-label="<?= Helpers::e(I18n::t('system_users.col_permissions')) ?>"><?= Helpers::e(I18n::t('system_users.role_desc_content_manager')) ?></td>
      </tr>
      <tr>
        <td class="creator" data-label="<?= Helpers::e(I18n::t('system_users.col_role')) ?>"><?= Helpers::e(Auth::roleLabel('reviewer')) ?></td>
        <td class="time-cell role-desc" data-label="<?= Helpers::e(I18n::t('system_users.col_permissions')) ?>"><?= Helpers::e(I18n::t('system_users.role_desc_reviewer')) ?></td>
      </tr>
      <tr>
        <td class="creator" data-label="<?= Helpers::e(I18n::t('system_users.col_role')) ?>"><?= Helpers::e(Auth::roleLabel('demo')) ?></td>
        <td class="time-cell role-desc" data-label="<?= Helpers::e(I18n::t('system_users.col_permissions')) ?>"><?= Helpers::e(I18n::t('system_users.role_desc_demo')) ?></td>
      </tr>
    </tbody>
  </table>
</div>

<?php if ($flash): ?>
  <div class="form-section" style="border-color:<?= $flash['type'] === 'success' ? 'var(--signal-dim)' : 'var(--danger)' ?>; margin-bottom:16px;">
    <p style="color:<?= $flash['type'] === 'success' ? 'var(--signal)' : 'var(--danger)' ?>; font-size:13px; margin:0;"><?= Helpers::e($flash['message']) ?></p>
  </div>
<?php endif; ?>

<div class="row-2 row-2-list-form">
  <div class="panel" style="overflow:visible;">
    <p class="panel-title"><?= $isMasterAdmin || $isDemo ? Helpers::e(I18n::t('system_users.panel_all_users')) : Helpers::e(I18n::t('system_users.panel_managers_reviewers')) ?></p>
    <table>
      <thead><tr><th><?= Helpers::e(I18n::t('system_users.col_username')) ?></th><th><?= Helpers::e(I18n::t('system_users.col_email')) ?></th><th><?= Helpers::e(I18n::t('system_users.col_role')) ?></th><th><?= Helpers::e(I18n::t('system_users.col_2fa')) ?></th><th><?= Helpers::e(I18n::t('system_users.col_status')) ?></th><th></th></tr></thead>
      <tbody>
        <?php foreach ($admins as $a): ?>
        <tr>
          <td class="creator" data-label="<?= Helpers::e(I18n::t('system_users.col_username')) ?>"><?= Helpers::e(DemoMask::name($a['username'])) ?><?= !$isDemo && (int) $a['id'] === (int) $viewer['id'] ? ' ' . Helpers::e(I18n::t('system_users.current_user_suffix')) : '' ?></td>
          <td class="time-cell" data-label="<?= Helpers::e(I18n::t('system_users.col_email')) ?>"><?= Helpers::e(DemoMask::email($a['email'])) ?></td>
          <td data-label="<?= Helpers::e(I18n::t('system_users.col_role')) ?>"><span class="plat-tag"><?= Helpers::e(Auth::roleLabel($a['role'])) ?></span></td>
          <td class="time-cell" data-label="<?= Helpers::e(I18n::t('system_users.col_2fa')) ?>"><?= $a['two_factor_method'] === 'none' ? '—' : (empty($a['two_factor_disabled_at']) ? '✓' : Helpers::e(I18n::t('system_users.status_2fa_suspended'))) ?></td>
          <td data-label="<?= Helpers::e(I18n::t('system_users.col_status')) ?>">
            <span class="plat-tag" style="<?= !$a['is_active'] ? 'background:rgba(232,115,95,0.12); color:var(--danger);' : '' ?>">
              <?= $a['is_active'] ? Helpers::e(I18n::t('system_users.status_active')) : Helpers::e(I18n::t('system_users.status_disabled')) ?>
            </span>
          </td>
          <td style="white-space:nowrap;">
            <?php $canShowManage = (int) $a['id'] !== (int) $viewer['id'] && ($isDemo || in_array($a['role'], $manageableRoles, true)); ?>
            <?php if ($canShowManage): ?>
              <button type="button" class="manage-toggle-btn" onclick="kvToggleManageRow(this)" style="cursor:pointer; color:var(--signal); font-size:12px; background:none; border:none; padding:0; font-family:inherit;"><?= Helpers::e(I18n::t('system_users.manage_button')) ?></button>
            <?php endif; ?>
          </td>
        </tr>
        <?php if ($canShowManage):
          $fSave = "mf-save-{$a['id']}";
          $fReset = "mf-reset-{$a['id']}";
          $fDelete = "mf-delete-{$a['id']}";
          $hasTwoFactor = $a['two_factor_method'] !== 'none';
          $twoFactorSuspended = $hasTwoFactor && !empty($a['two_factor_disabled_at']);
        ?>
        <tr class="manage-row" style="display:none;">
          <td colspan="6" class="manage-cell" style="padding:0; border-bottom:1px solid var(--line-soft);">
            <div class="manage-panel">
              <fieldset <?= $isDemo ? 'disabled' : '' ?> style="border:0; margin:0; padding:0;">
                <!-- Every action is still its own real <form> (so each POST only
                     ever carries exactly the fields for that one action) -- they
                     just render with no visible box of their own, and every
                     button below is associated to its form via the HTML5 `form`
                     attribute instead of nesting inside it. That's what lets the
                     inputs all live together in one group and the buttons in
                     another, purely a visual arrangement with no bearing on
                     which fields actually get submitted where. -->
                <form id="<?= $fSave ?>" method="post">
                  <?= Csrf::field() ?>
                  <input type="hidden" name="form_action" value="save_account">
                  <input type="hidden" name="id" value="<?= (int) $a['id'] ?>">
                </form>
                <form id="<?= $fReset ?>" method="post" onsubmit="return confirm(<?= json_encode(I18n::t('system_users.confirm_reset_password_js'), JSON_HEX_APOS | JSON_HEX_QUOT) ?>);">
                  <?= Csrf::field() ?>
                  <input type="hidden" name="form_action" value="reset_password">
                  <input type="hidden" name="id" value="<?= (int) $a['id'] ?>">
                </form>
                <form id="<?= $fDelete ?>" method="post" onsubmit="return confirm(<?= json_encode(I18n::t('system_users.confirm_delete_account_js'), JSON_HEX_APOS | JSON_HEX_QUOT) ?>);">
                  <?= Csrf::field() ?>
                  <input type="hidden" name="form_action" value="delete">
                  <input type="hidden" name="id" value="<?= (int) $a['id'] ?>">
                </form>

                <div class="manage-fields">
                  <input type="text" form="<?= $fSave ?>" name="new_username" value="<?= Helpers::e($a['username']) ?>" placeholder="<?= Helpers::e(I18n::t('system_users.col_username')) ?>" required>
                  <input type="email" form="<?= $fSave ?>" name="new_email" value="<?= Helpers::e($a['email']) ?>" placeholder="<?= Helpers::e(I18n::t('system_users.col_email')) ?>" required>
                  <select form="<?= $fSave ?>" name="new_role">
                    <?php foreach (($isDemo ? $visibleRoles : $manageableRoles) as $r): ?>
                      <option value="<?= Helpers::e($r) ?>" <?= $r === $a['role'] ? 'selected' : '' ?>><?= Helpers::e(Auth::roleLabel($r)) ?></option>
                    <?php endforeach; ?>
                  </select>
                  <?php if ($hasTwoFactor): ?>
                  <select form="<?= $fSave ?>" name="two_factor_state" title="<?= Helpers::e(I18n::t('system_users.two_factor_restore_hint')) ?>">
                    <option value="active" <?= !$twoFactorSuspended ? 'selected' : '' ?>><?= Helpers::e(I18n::t('system_users.opt_2fa_enabled')) ?></option>
                    <option value="disabled" <?= $twoFactorSuspended ? 'selected' : '' ?>><?= Helpers::e(I18n::t('system_users.opt_2fa_disabled')) ?></option>
                  </select>
                  <?php else: ?>
                  <span class="manage-field-note"><?= Helpers::e(I18n::t('system_users.status_2fa_not_set_up')) ?></span>
                  <?php endif; ?>
                  <select form="<?= $fSave ?>" name="account_status">
                    <option value="active" <?= $a['is_active'] ? 'selected' : '' ?>><?= Helpers::e(I18n::t('system_users.opt_account_enabled')) ?></option>
                    <option value="disabled" <?= !$a['is_active'] ? 'selected' : '' ?>><?= Helpers::e(I18n::t('system_users.opt_account_disabled')) ?></option>
                  </select>
                </div>
                <div class="manage-buttons">
                  <button type="submit" form="<?= $fDelete ?>" class="btn btn-ghost manage-btn-delete" style="color:var(--danger);"><?= Helpers::e(I18n::t('system_users.delete_account_button')) ?></button>
                  <button type="submit" form="<?= $fReset ?>" class="btn btn-ghost manage-btn-reset"><?= Helpers::e(I18n::t('system_users.reset_password_button')) ?></button>
                  <button type="submit" form="<?= $fSave ?>" class="btn btn-primary manage-btn-save"><?= Helpers::e(I18n::t('system_users.save_button')) ?></button>
                </div>
              </fieldset>
            </div>
          </td>
        </tr>
        <?php endif; ?>
        <?php endforeach; ?>
      </tbody>
    </table>
  </div>

  <div class="panel">
    <p class="panel-title"><?= Helpers::e(I18n::t('system_users.new_user_panel_title')) ?></p>
    <form method="post">
      <?= Csrf::field() ?>
      <input type="hidden" name="form_action" value="create">
      <fieldset <?= $isDemo ? 'disabled' : '' ?> style="border:0; margin:0; padding:0;">
      <div class="field"><label><?= Helpers::e(I18n::t('system_users.col_username')) ?></label><input type="text" name="username" required></div>
      <div class="field" id="new-user-email-field"><label><?= Helpers::e(I18n::t('system_users.col_email')) ?></label><input type="email" name="email" id="new-user-email" required></div>
      <div class="field">
        <label id="new-user-password-label"><?= Helpers::e(I18n::t('system_users.field_password_hint')) ?></label>
        <input type="password" name="password" id="new-user-password" minlength="10" required>
      </div>
      <div class="field">
        <label><?= Helpers::e(I18n::t('system_users.col_role')) ?></label>
        <select name="role" id="new-user-role" required onchange="(function(sel){
          var isDemo = sel.value === 'demo';
          var pw = document.getElementById('new-user-password');
          var lbl = document.getElementById('new-user-password-label');
          if (isDemo) { pw.removeAttribute('minlength'); lbl.textContent = <?= json_encode(I18n::t('system_users.field_password'), JSON_HEX_APOS | JSON_HEX_QUOT) ?>; }
          else { pw.setAttribute('minlength', '10'); lbl.textContent = <?= json_encode(I18n::t('system_users.field_password_hint'), JSON_HEX_APOS | JSON_HEX_QUOT) ?>; }
          var emailField = document.getElementById('new-user-email-field');
          var email = document.getElementById('new-user-email');
          if (isDemo) { emailField.style.display = 'none'; email.removeAttribute('required'); }
          else { emailField.style.display = ''; email.setAttribute('required', 'required'); }
        })(this)">
          <?php foreach ($visibleRoles as $r): ?>
            <option value="<?= Helpers::e($r) ?>"><?= Helpers::e(Auth::roleLabel($r)) ?></option>
          <?php endforeach; ?>
        </select>
      </div>
      <div class="form-actions" style="padding-bottom:0;"><button type="submit" class="btn btn-primary"><?= Helpers::e(I18n::t('system_users.create_user_button')) ?></button></div>
      </fieldset>
    </form>
  </div>
</div>

<script>
  // "Manage" used to open a position:absolute popup anchored to the
  // account's row -- compact, but it floated free of its own card
  // (and past its own table row's bottom edge) on any screen narrow or
  // short enough that there wasn't room below the trigger, since an
  // absolutely-positioned element doesn't count toward its container's
  // size. A genuine extra <tr> right after the account's row instead
  // makes the table (or, stacked, the row's own card on mobile) grow to
  // actually contain it -- nothing to float past or overflow.
  function kvToggleManageRow(btn) {
    var row = btn.closest('tr').nextElementSibling;
    if (row && row.classList.contains('manage-row')) {
      row.style.display = row.style.display === 'none' ? '' : 'none';
    }
  }
</script>

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