<?php
/**
 * Roles & default features (SysAdmin only).  roles-v1
 *
 * One screen to see, PER ROLE, which default features (permissions) it grants — and add / edit / remove them.
 * Editing a role's defaults flows to EVERY user who holds that role (existing AND newly created), except where a
 * per-user Grant/Deny override is set on the Permissions page (admin/access-control.php → user_permissions).
 *
 * Scope of this page = the role_permissions table only. Per-user deviations live on access-control.php; data
 * access (client » process » facility » provider) lives on user-alignment.php. The 'sysadmin' role is the '*'
 * superuser (bypasses every check) and is therefore shown read-only.
 */
require_once __DIR__ . '/../includes/config.php';
$user = require_login('internal');
$pdo  = db();
$uid  = (int) $user['id'];

$isSysadmin = user_has_role($uid, 'sysadmin');
if (!$isSysadmin) { http_response_code(403); exit('Only SysAdmin can edit role default features.'); }

/* ───────────────────────── POST: save ONE role's default permission set ───────────────────────── */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_role_perms') {
    csrf_check_or_die();
    $rid  = (int) ($_POST['role_id'] ?? 0);
    $role = null;
    if ($rid) { $q = $pdo->prepare("SELECT id,code,name FROM roles WHERE id=?"); $q->execute([$rid]); $role = $q->fetch() ?: null; }
    if (!$role) { flash('error', 'Role not found.'); redirect('/admin/roles.php'); }
    // The SysAdmin role is the superuser ('*') — its permission rows are irrelevant (it bypasses can()) and
    // editing it would only mislead, so it is never writable here.
    if ($role['code'] === 'sysadmin') { flash('error', 'The SysAdmin role has full access and cannot be edited.'); redirect('/admin/roles.php?role_id=' . $rid); }

    // Resolve POSTed permission CODES → ids (ignore anything not a real permission).
    $codesIn = (array) ($_POST['codes'] ?? []);
    $valid   = $pdo->query("SELECT code,id FROM permissions")->fetchAll(PDO::FETCH_KEY_PAIR);
    $ids = [];
    foreach ($codesIn as $c) { if (isset($valid[$c])) $ids[(int) $valid[$c]] = true; }

    // The page-centric editor only shows codes represented by a catalog PAGE. PRESERVE any permission the role
    // already holds that is NOT on the page (a capability not tied to a feature-page) — otherwise saving would
    // silently strip it. Only codes in the "managed" (catalog) set are governed by the checkboxes.
    $managed = [];
    $collect = function (array $pg) use (&$managed) {
        foreach (array_merge($pg['gate'] ?? [], $pg['also'] ?? []) as $c) if ($c !== '__sysadmin__' && $c !== '__masterdata__') $managed[$c] = 1;
        if (!empty($pg['file']) && isset(medmecs_grantable_pages()[$pg['file']])) $managed[page_perm_code($pg['file'])] = 1;
    };
    $cat = medmecs_page_catalog();
    foreach ($cat['verticals'] as $pg) $collect($pg);
    foreach ($cat['master']    as $pg) $collect($pg);
    foreach ($cat['reports']   as $pg) $collect($pg);
    foreach ($cat['platform']  as $grp) foreach ($grp['pages'] as $pg) $collect($pg);
    $cur = $pdo->prepare("SELECT pm.code FROM role_permissions rp JOIN permissions pm ON pm.id=rp.permission_id WHERE rp.role_id=?");
    $cur->execute([$rid]);
    foreach ($cur->fetchAll(PDO::FETCH_COLUMN) as $c) if (!isset($managed[$c]) && isset($valid[$c])) $ids[(int) $valid[$c]] = true;   // keep off-page codes
    $ids = array_keys($ids);

    $pdo->beginTransaction();
    $pdo->prepare("DELETE FROM role_permissions WHERE role_id=?")->execute([$rid]);
    if ($ids) {
        $ins = $pdo->prepare("INSERT IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)");
        foreach ($ids as $pid) $ins->execute([$rid, (int) $pid]);
    }
    $pdo->commit();
    audit_log('role.permissions.set', 'role', $rid, null, ['code' => $role['code'], 'count' => count($ids)]);
    flash('success', 'Default features updated for ' . $role['name'] . ' (' . count($ids) . ') — applies to everyone with this role.');
    redirect('/admin/roles.php?role_id=' . $rid);
}

/* ───────────────────────── POST: create a NEW custom role ───────────────────────── */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'create_role') {
    csrf_check_or_die();
    $name  = trim((string) ($_POST['name'] ?? ''));
    $desc  = trim((string) ($_POST['description'] ?? ''));
    // internal XOR client only — the merged Users form derives the login type from a role's scope, and a 'both'
    // role can't unambiguously create a client login, so we don't offer it.
    $scope = (($_POST['scope'] ?? '') === 'client') ? 'client' : 'internal';
    // Code: explicit if given, else slugified from the name. Lowercase, non-alphanumeric → underscore.
    $code  = trim((string) ($_POST['code'] ?? '')) ?: $name;
    $code  = trim(strtolower(preg_replace('/[^a-z0-9]+/i', '_', $code)), '_');
    if (strlen($code) > 50) $code = substr($code, 0, 50);
    if ($name === '' || $code === '') { flash('error', 'A role name is required.'); redirect('/admin/roles.php'); }
    if ($code === 'sysadmin')         { flash('error', 'You cannot create another "sysadmin" role.'); redirect('/admin/roles.php'); }
    $ex = $pdo->prepare("SELECT id FROM roles WHERE code=?"); $ex->execute([$code]);
    if ($ex->fetchColumn()) { flash('error', 'A role with code "' . $code . '" already exists — pick another name/code.'); redirect('/admin/roles.php'); }
    $pdo->prepare("INSERT INTO roles (code,name,description,is_system,scope,created_at,updated_at) VALUES (?,?,?,0,?,NOW(),NOW())")
        ->execute([$code, $name, $desc ?: null, $scope]);
    $newId = (int) $pdo->lastInsertId();
    audit_log('role.create', 'role', $newId, null, ['code' => $code, 'scope' => $scope]);
    flash('success', 'Role "' . $name . '" created — now tick its default features below.');
    redirect('/admin/roles.php?role_id=' . $newId);
}

/* ───────────────────────── POST: delete a custom (non-system, unused) role ───────────────────────── */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete_role') {
    csrf_check_or_die();
    $rid  = (int) ($_POST['role_id'] ?? 0);
    $q = $pdo->prepare("SELECT id,code,name,is_system FROM roles WHERE id=?"); $q->execute([$rid]); $role = $q->fetch() ?: null;
    if (!$role)                { flash('error', 'Role not found.'); redirect('/admin/roles.php'); }
    if ((int) $role['is_system']) { flash('error', 'Built-in system roles cannot be deleted.'); redirect('/admin/roles.php?role_id=' . $rid); }
    $uc = $pdo->prepare("SELECT COUNT(*) FROM user_roles WHERE role_id=?"); $uc->execute([$rid]);
    if ((int) $uc->fetchColumn() > 0) { flash('error', 'That role is still assigned to users — remove it from them first.'); redirect('/admin/roles.php?role_id=' . $rid); }
    $pdo->beginTransaction();
    $pdo->prepare("DELETE FROM role_permissions WHERE role_id=?")->execute([$rid]);
    $pdo->prepare("DELETE FROM roles WHERE id=? AND is_system=0")->execute([$rid]);
    $pdo->commit();
    audit_log('role.delete', 'role', $rid, null, ['code' => $role['code']]);
    flash('success', 'Role "' . $role['name'] . '" deleted.');
    redirect('/admin/roles.php');
}

/* ───────────────────────── GET ───────────────────────── */
$roles = $pdo->query("SELECT r.id,r.code,r.name,r.description,r.scope,r.is_system,
        (SELECT COUNT(*) FROM role_permissions rp WHERE rp.role_id=r.id) perm_count,
        (SELECT COUNT(*) FROM user_roles ur WHERE ur.role_id=r.id) user_count
    FROM roles r ORDER BY r.scope, r.name")->fetchAll();

// Permission catalog grouped by area — same grouping/labels as the Permissions page so the two read alike.
$GROUP_LABELS = ['coding'=>'Coding — vertical', 'ar'=>'AR (Accounts Receivable) — vertical', 'billing'=>'Billing — vertical',
    'reports'=>'Report Access — Reports platform', 'access'=>'Report Access — Grant management',
    'master'=>'Master Data — shared across verticals', 'ops'=>'Settings & Operations', 'users'=>'User &amp; role management'];
$GROUP_ICONS  = ['coding'=>'stethoscope', 'ar'=>'cash', 'billing'=>'file-invoice', 'reports'=>'report-analytics', 'access'=>'key',
    'master'=>'database', 'ops'=>'settings', 'users'=>'users'];
$GROUP_ORDER  = ['coding', 'ar', 'billing', 'reports', 'access', 'master', 'ops', 'users'];
$permsByGroup = [];
foreach ($pdo->query("SELECT code, label, COALESCE(NULLIF(description,''),'') descr, COALESCE(NULLIF(group_name,''), SUBSTRING_INDEX(code,'.',1)) g FROM permissions ORDER BY label") as $p)
    $permsByGroup[$p['g']][] = $p;
uksort($permsByGroup, function ($a, $b) use ($GROUP_ORDER) {
    $ia = array_search($a, $GROUP_ORDER, true); $ib = array_search($b, $GROUP_ORDER, true);
    return (($ia === false ? 99 : $ia) <=> ($ib === false ? 99 : $ib)) ?: strcmp($a, $b);
});
$totalPerms = (int) $pdo->query("SELECT COUNT(*) FROM permissions")->fetchColumn();

// Selected role to edit.
$selRoleId = (int) ($_GET['role_id'] ?? 0);
$selRole = null; $roleCodes = [];
if ($selRoleId) {
    foreach ($roles as $r) if ((int) $r['id'] === $selRoleId) $selRole = $r;
    if ($selRole) {
        $rc = $pdo->prepare("SELECT pm.code FROM role_permissions rp JOIN permissions pm ON pm.id=rp.permission_id WHERE rp.role_id=?");
        $rc->execute([$selRoleId]); $roleCodes = array_flip($rc->fetchAll(PDO::FETCH_COLUMN));
    }
}
$selIsSysadmin = $selRole && $selRole['code'] === 'sysadmin';

$pageTitle = 'Roles & features';
require __DIR__ . '/../includes/header.php';
?>
<style>
  .rl-cols{ column-count:2; column-gap:1.25rem; }
  @media (max-width: 992px){ .rl-cols{ column-count:1; } }
  .rl-sec{ break-inside:avoid; }
  .rl-page{ padding:.4rem 0; border-top:1px solid var(--tblr-border-color,#e6e7e9); }
  .rl-page:first-child{ border-top:0; }
  .rl-page-head{ font-size:.82rem; line-height:1.35; }
  .rl-code{ font-size:.76rem; margin-left:1.15rem; }
  .rl-code code, .rl-page-head code{ font-size:.64rem; }
  .rl-syslock{ margin-left:1.15rem; }
</style>

<div class="page-header"><div class="container-xl"><div class="row align-items-center">
    <div class="col">
        <h2 class="page-title"><i class="ti ti-shield-cog me-2 text-primary"></i>Roles &amp; default features</h2>
        <div class="text-secondary">The <strong>default features</strong> (permissions) each role grants. Edit a role and every user with that role — existing and newly created — gets the change, unless a <a href="<?= e(url('/admin/access-control.php')) ?>">per-user override</a> says otherwise.</div>
    </div>
    <div class="col-auto d-flex gap-2">
        <button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#newRoleForm"><i class="ti ti-plus me-1"></i>New role</button>
        <a href="<?= e(url('/admin/access-control.php')) ?>" class="btn btn-outline-secondary"><i class="ti ti-lock-cog me-1"></i>Per-user permissions</a>
        <a href="<?= e(url('/admin/users.php')) ?>" class="btn btn-outline-secondary"><i class="ti ti-user-cog me-1"></i>Users</a>
    </div>
</div></div></div>

<div class="page-body"><div class="container-xl">

    <!-- ───────── CREATE A NEW ROLE (collapsed by default) ───────── -->
    <div class="collapse mb-3" id="newRoleForm">
        <form method="POST" action="<?= e(url('/admin/roles.php')) ?>" class="card">
            <?= csrf_field() ?>
            <input type="hidden" name="action" value="create_role">
            <div class="card-header"><h3 class="card-title"><i class="ti ti-shield-plus me-2"></i>Create a new role</h3></div>
            <div class="card-body row g-3">
                <div class="col-md-4">
                    <label class="form-label required">Role name</label>
                    <input type="text" name="name" class="form-control" required maxlength="100" placeholder="e.g. Client Auditor">
                </div>
                <div class="col-md-3">
                    <label class="form-label">Code <span class="text-secondary">(optional)</span></label>
                    <input type="text" name="code" class="form-control" maxlength="50" placeholder="auto from name">
                    <div class="form-hint">Lowercase id used internally. Leave blank to derive from the name.</div>
                </div>
                <div class="col-md-3">
                    <label class="form-label required">Scope</label>
                    <select name="scope" class="form-select">
                        <option value="internal">Internal (staff login)</option>
                        <option value="client">Client (portal login)</option>
                    </select>
                    <div class="form-hint">A client-scope role makes a client-portal login in the Users screen.</div>
                </div>
                <div class="col-md-2 align-self-end">
                    <button class="btn btn-primary w-100"><i class="ti ti-check me-1"></i>Create</button>
                </div>
                <div class="col-12">
                    <label class="form-label">Description</label>
                    <input type="text" name="description" class="form-control" maxlength="255" placeholder="What is this role for?">
                </div>
                <div class="col-12"><div class="text-secondary small"><i class="ti ti-info-circle me-1"></i>After creating, you'll pick the role's default features (permissions) here. Assign the role to people on the Users screen.</div></div>
            </div>
        </form>
    </div>

    <!-- ───────── ROLE OVERVIEW ───────── -->
    <div class="card mb-3">
        <div class="card-header"><h3 class="card-title"><i class="ti ti-list-check me-2"></i>Roles</h3>
            <div class="card-actions text-secondary small">Pick a role to add / remove its default features</div></div>
        <div class="table-responsive"><table class="table table-vcenter card-table">
            <thead><tr><th>Role</th><th>Scope</th><th class="text-end">Default features</th><th class="text-end">Users with role</th><th class="w-1"></th></tr></thead>
            <tbody>
            <?php foreach ($roles as $r): $isSa = $r['code']==='sysadmin'; ?>
                <tr<?= $selRoleId===(int)$r['id']?' class="table-active"':'' ?>>
                    <td><strong><?= e($r['name']) ?></strong> <code class="text-secondary" style="font-size:.66rem"><?= e($r['code']) ?></code>
                        <?php if ($r['description']): ?><div class="text-secondary small"><?= e($r['description']) ?></div><?php endif; ?></td>
                    <td><span class="badge bg-<?= $r['scope']==='client'?'azure':'purple' ?>-lt"><?= e($r['scope']) ?></span></td>
                    <td class="text-end"><?= $isSa ? '<span class="badge bg-secondary-lt">all ('.number_format($totalPerms).')</span>' : '<span class="fw-bold">'.number_format((int)$r['perm_count']).'</span> <span class="text-secondary small">/ '.number_format($totalPerms).'</span>' ?></td>
                    <td class="text-end text-secondary"><?= number_format((int)$r['user_count']) ?></td>
                    <td class="text-end">
                        <?php if ($isSa): ?><span class="text-secondary small">full access</span>
                        <?php else: ?>
                            <div class="btn-list justify-content-end flex-nowrap">
                                <a href="<?= e(url('/admin/roles.php?role_id='.(int)$r['id'])) ?>" class="btn btn-sm <?= $selRoleId===(int)$r['id']?'btn-primary':'btn-outline-primary' ?>"><i class="ti ti-edit me-1"></i>Edit defaults</a>
                                <?php if (!(int)$r['is_system'] && (int)$r['user_count']===0): ?>
                                    <form method="POST" action="<?= e(url('/admin/roles.php')) ?>" onsubmit="return confirm('Delete the role &quot;<?= e($r['name']) ?>&quot;? This cannot be undone.');">
                                        <?= csrf_field() ?><input type="hidden" name="action" value="delete_role"><input type="hidden" name="role_id" value="<?= (int)$r['id'] ?>">
                                        <button class="btn btn-sm btn-ghost-danger" title="Delete this custom role"><i class="ti ti-trash"></i></button>
                                    </form>
                                <?php elseif (!(int)$r['is_system']): ?>
                                    <span class="text-secondary small" title="In use by <?= (int)$r['user_count'] ?> user(s) — unassign first">in use</span>
                                <?php else: ?>
                                    <span class="badge bg-secondary-lt" title="Built-in role">system</span>
                                <?php endif; ?>
                            </div>
                        <?php endif; ?>
                    </td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table></div>
    </div>

    <?php if ($selRole && $selIsSysadmin): ?>
        <div class="alert alert-secondary"><i class="ti ti-shield-lock me-1"></i><strong>SysAdmin</strong> has full access to everything by design — its default features are not editable.</div>
    <?php elseif ($selRole): ?>
        <!-- ───────── EDITOR for the selected role ───────── -->
        <form method="POST" action="<?= e(url('/admin/roles.php')) ?>" id="roleForm">
            <?= csrf_field() ?>
            <input type="hidden" name="action" value="save_role_perms">
            <input type="hidden" name="role_id" value="<?= (int)$selRole['id'] ?>">

            <div class="card mb-3"><div class="card-body d-flex flex-wrap gap-3 align-items-center">
                <div><div class="h3 mb-0"><?= e($selRole['name']) ?> <span class="badge bg-<?= $selRole['scope']==='client'?'azure':'purple' ?>-lt ms-1"><?= e($selRole['scope']) ?></span></div>
                    <div class="text-secondary small"><code><?= e($selRole['code']) ?></code> · <?= number_format((int)$selRole['user_count']) ?> user(s) hold this role</div></div>
                <div class="ms-auto d-flex flex-wrap gap-2 align-items-center">
                    <input type="search" id="rl_search" class="form-control form-control-sm" style="max-width:230px" placeholder="Filter features…" autocomplete="off">
                    <span class="text-secondary small"><span id="rl_count"><?= number_format(count($roleCodes)) ?></span> selected</span>
                    <button class="btn btn-primary"><i class="ti ti-device-floppy me-1"></i>Save defaults</button>
                </div>
            </div></div>

            <div class="alert alert-info py-2 px-3 small"><i class="ti ti-info-circle me-1"></i>Every <strong>feature page</strong> is listed under its module, with the permission(s) that unlock it. Tick a permission to make it a <strong>default</strong> for this role — the page's badge turns green once the role grants access. Saving updates <strong>every user</strong> with the <strong><?= e($selRole['name']) ?></strong> role (and is the starting point for new users), except where a per-user override is set on the <a href="<?= e(url('/admin/access-control.php')) ?>">Permissions</a> page.</div>

            <?php
            $cat = medmecs_page_catalog();
            $permLabels = []; foreach ($permsByGroup as $__l) foreach ($__l as $__p) $permLabels[$__p['code']] = $__p['label'];
            // One PAGE: its header (icon/label/file + a live "role grants access" badge) + its grantable permission
            // checkboxes. A code that unlocks several pages appears under each; JS keeps every instance in sync so the
            // saved set (deduped server-side) is unambiguous. SysAdmin-only pages are shown read-only (not grantable).
            $renderPage = function (array $pg) use ($roleCodes, $permLabels) {
                $gate = $pg['gate'] ?? []; $also = $pg['also'] ?? [];
                $sysOnly = in_array('__sysadmin__', $gate, true);
                $gateReal = array_values(array_filter($gate, fn($c) => $c !== '__sysadmin__' && $c !== '__masterdata__'));
                $pageCode = (!$sysOnly && !empty($pg['file']) && isset(medmecs_grantable_pages()[$pg['file']])) ? page_perm_code($pg['file']) : null;
                $accessCodes = $pageCode ? array_merge([$pageCode], $gateReal) : $gateReal;   // ANY of these = page access
                $codes = array_values(array_unique(array_merge($pageCode ? [$pageCode] : [], $gateReal, $also)));
                $hasAccess = $sysOnly ? null : (bool) array_filter($accessCodes, fn($c) => isset($roleCodes[$c]));
                ?>
                <div class="rl-page" data-gates="<?= e(implode(',', $accessCodes)) ?>" data-hay="<?= e(mb_strtolower(strip_tags(str_replace('&amp;','&',$pg['label'])).' '.($pg['file']??'').' '.implode(' ', $codes))) ?>">
                    <div class="rl-page-head">
                        <i class="ti ti-<?= e($pg['icon'] ?? 'file') ?> me-1 text-secondary"></i><span class="fw-bold"><?= $pg['label'] /* trusted catalog string (may hold entities) */ ?></span>
                        <?php if (!empty($pg['file'])): ?> <code class="text-secondary"><?= e($pg['file']) ?></code><?php endif; ?>
                        <?php if (!empty($pg['note'])): ?> <span class="text-secondary" style="font-size:.7rem">— <?= $pg['note'] ?></span><?php endif; ?>
                        <?php if ($sysOnly): ?><span class="badge bg-purple-lt ms-2">SysAdmin only</span>
                        <?php elseif ($hasAccess !== null): ?><span class="rl-access badge ms-2 bg-<?= $hasAccess?'green':'secondary' ?>-lt"><?= $hasAccess?'Role grants access':'No access' ?></span><?php endif; ?>
                        <?php if (!empty($pg['shared'])): ?><span class="badge bg-azure-lt ms-1" title="Shared master data — the same permission everywhere">shared</span><?php endif; ?>
                    </div>
                    <?php if ($sysOnly): ?>
                        <div class="rl-syslock text-secondary small">Fixed to SysAdmin — not grantable to a role.</div>
                    <?php else: foreach ($codes as $c): $has = isset($roleCodes[$c]); ?>
                        <label class="form-check rl-code mb-0">
                            <input type="checkbox" class="form-check-input rl-cb" name="codes[]" value="<?= e($c) ?>" data-code="<?= e($c) ?>" <?= $has?'checked':'' ?>>
                            <span class="form-check-label"><?= e($permLabels[$c] ?? $c) ?> <code class="text-secondary"><?= e($c) ?></code></span>
                        </label>
                    <?php endforeach; endif; ?>
                </div>
                <?php
            };
            // One section card (a titled group of pages) with a "select all shown" convenience toggle.
            $renderSection = function (string $title, string $icon, array $pages) use ($renderPage) { ?>
                <div class="card mb-3 rl-sec" data-section>
                    <div class="card-header py-2 d-flex align-items-center">
                        <i class="ti ti-<?= e($icon) ?> me-2 text-secondary"></i><h3 class="card-title mb-0"><?= $title /* trusted */ ?></h3>
                        <label class="form-check form-check-inline ms-auto mb-0 small text-secondary"><input type="checkbox" class="form-check-input rl-secall"> all shown</label>
                    </div>
                    <div class="card-body py-2"><?php foreach ($pages as $pg) $renderPage($pg); ?></div>
                </div>
                <?php
            };
            $VLABEL = ['coding'=>'Coding — vertical', 'ar'=>'AR (Accounts Receivable) — vertical', 'billing'=>'Billing — vertical'];
            $VICON  = ['coding'=>'stethoscope', 'ar'=>'cash', 'billing'=>'file-invoice'];
            ?>
            <div class="rl-cols">
                <?php foreach (['coding','ar','billing'] as $sec):
                    $renderSection($VLABEL[$sec], $VICON[$sec], array_values(array_filter($cat['verticals'], fn($p) => $p['sec']===$sec)));
                endforeach; ?>
                <?php $renderSection('Master data — shared across verticals', 'database', $cat['master']); ?>
                <?php $renderSection('Report Access — Reports platform', 'report-analytics', $cat['reports']); ?>
                <?php foreach ($cat['platform'] as $grp) $renderSection($grp['title'], $grp['icon'], $grp['pages']); ?>
            </div>

            <div class="text-end mb-4"><button class="btn btn-primary"><i class="ti ti-device-floppy me-1"></i>Save defaults for <?= e($selRole['name']) ?></button></div>
        </form>

        <script>
        (function(){
            var form=document.getElementById('roleForm'); if(!form) return;
            var cbs=function(){ return Array.prototype.slice.call(form.querySelectorAll('.rl-cb')); };
            var countEl=document.getElementById('rl_count');
            function uniqueChecked(){ var set={}; cbs().forEach(function(c){ if(c.checked) set[c.dataset.code]=1; }); return Object.keys(set).length; }
            function recount(){ if(countEl) countEl.textContent=uniqueChecked().toLocaleString(); }
            function syncCode(code,on){ cbs().forEach(function(c){ if(c.dataset.code===code) c.checked=on; }); }
            function refreshBadges(){
                var checked={}; cbs().forEach(function(c){ if(c.checked) checked[c.dataset.code]=1; });
                form.querySelectorAll('.rl-page').forEach(function(pg){
                    var b=pg.querySelector('.rl-access'); if(!b) return;
                    var gates=(pg.dataset.gates||'').split(',').filter(Boolean);
                    var has=gates.some(function(g){ return checked[g]; });
                    b.className='rl-access badge ms-2 bg-'+(has?'green':'secondary')+'-lt'; b.textContent=has?'Role grants access':'No access';
                });
            }
            form.addEventListener('change', function(e){
                if(!e.target.classList || !e.target.classList.contains('rl-cb')) return;
                syncCode(e.target.dataset.code, e.target.checked); recount(); refreshBadges();
            });
            // per-section "all shown" toggle
            form.querySelectorAll('[data-section]').forEach(function(card){
                var all=card.querySelector('.rl-secall'); if(!all) return;
                all.addEventListener('change', function(){
                    card.querySelectorAll('.rl-page').forEach(function(pg){
                        if(pg.style.display==='none') return;
                        pg.querySelectorAll('.rl-cb').forEach(function(c){ c.checked=all.checked; syncCode(c.dataset.code, all.checked); });
                    });
                    recount(); refreshBadges();
                });
            });
            // filter across pages
            var s=document.getElementById('rl_search');
            if(s) s.addEventListener('input', function(){
                var q=(this.value||'').toLowerCase().trim();
                form.querySelectorAll('.rl-page').forEach(function(pg){ pg.style.display=(!q||(pg.dataset.hay||'').indexOf(q)!==-1)?'':'none'; });
                form.querySelectorAll('[data-section]').forEach(function(card){
                    var any=Array.prototype.some.call(card.querySelectorAll('.rl-page'), function(pg){return pg.style.display!=='none';});
                    card.style.display=any?'':'none';
                });
            });
            recount(); refreshBadges();
        })();
        </script>
    <?php else: ?>
        <div class="card"><div class="card-body text-center text-secondary py-5">
            <i class="ti ti-shield-cog" style="font-size:2.2rem"></i>
            <div class="mt-2">Pick a role above to see and edit the default features new (and existing) users with that role receive.</div>
        </div></div>
    <?php endif; ?>

</div></div>
<?php require __DIR__ . '/../includes/footer.php'; ?>
