<?php
// roles_permissions.php — Manage Roles & Permissions (roles.rolename, roles.roledesc JSON)
// Update: delete uses its own confirm dialog; improved empty state & view layout; group select/deselect for sections & submenus.

declare(strict_types=1);
session_start();

// ===== DB =====
require_once __DIR__ . '/../../includes/connect.php'; // adjust if needed
if (!isset($pdo) || !($pdo instanceof PDO)) {
  die('Database connection ($pdo) not found. Ensure includes/connect.php initializes $pdo.');
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// ===== Minimal CSRF =====
if (empty($_SESSION['csrf'])) {
  $_SESSION['csrf'] = bin2hex(random_bytes(16));
}
$CSRF = $_SESSION['csrf'];

// ===== Permissions Schema (hierarchical) =====
$SCHEMA = [
  'property' => [
    'New Property' => 'property.new',
    'Property Search' => 'property.search',
    'Property View' => [
      'View Old Property Card' => 'property.view.oldCard',
      'View New Property' => 'property.view.newProperty',
      'View Abstract Book' => 'property.view.abstractBook',
      'Property Card Manager' => 'property.view.cardManager',
    ],
    'Survey Details' => 'property.surveyDetails',
    'Beacon Details' => [
      'Add New Coordinates' => 'property.beacon.addNew',
      'View Coordinate Register' => 'property.beacon.viewRegister',
      'Map Beacon Coordinates' => 'property.beacon.map',
    ],
    'Delete' => 'property.delete',
  ],
  'file' => [
    'New File' => [
      'Create New' => 'file.new.create',
      'Create From Old' => 'file.new.fromOld',
      'Create Temporary File' => 'file.new.temp',
      'Convert Temporary File' => 'file.new.convertTemp',
      'Create Sub File' => 'file.new.subFile',
    ],
    'View Files' => [
      'View All Files' => 'file.view.all',
      'View Deeds Files' => 'file.view.deeds',
      'View Recertification Files' => 'file.view.recert',
    ],
    'Snapshot' => [
      'Re-certification' => 'file.snapshot.recert',
      'Registry' => 'file.snapshot.registry',
    ],
    'Movement Log' => [
      'Log Movement' => 'file.movement.log',
      'View Movement Log' => 'file.movement.view',
    ],
    'Report' => 'file.report',
    'Comment' => [
      'Comment File' => 'file.comment.add',
      'View File Comments' => 'file.comment.view',
    ],
    'Delete' => [
      'Delete File' => 'file.delete.remove',
      'View Deleted' => 'file.delete.view',
    ],
    'Revoke' => [
      'Revoke File' => 'file.revoke.do',
      'View Revoke' => 'file.revoke.view',
    ],
    'Caveat' => [
      'Place Caveat' => 'file.caveat.place',
      'View Caveat' => 'file.caveat.view',
    ],
  ],
  'process' => [
    'Check List' => 'process.checklist',
    'Validate Record' => 'process.validateRecord',
    'Print Draft' => 'process.printDraft',
    'Validate' => 'process.validate',
    'Final' => 'process.final',
    'CTC' => 'process.ctc',
    'Re-Print' => 'process.reprint',
    'Reset' => [
      'Checklist' => 'process.reset.checklist',
      'Validate Record' => 'process.reset.validateRecord',
      'Print Draft Instrument' => 'process.reset.printDraftInstrument',
      'Validate Instrument' => 'process.reset.validateInstrument',
      'Validate Survey' => 'process.reset.validateSurvey',
    ],
    'Execute' => 'process.execute',
    'Dispatch' => 'process.dispatch',
  ],
  'payment' => [
    'Ground Rent' => [
      'Calculate Ground Rent' => 'payment.groundRent.calculate',
      'Register Ground Rent' => 'payment.groundRent.register',
    ],
    'Raise Invoice for POA' => 'payment.raiseInvoicePOA',
    'Register Payment' => 'payment.register',
    'View Ground Rent' => 'payment.viewGroundRent',
  ],
  'registry' => [
    'Instrument Transaction' => [
      'Process Transaction' => 'registry.instrument.process',
      'View Transactions' => 'registry.instrument.view',
      'Edit Transaction' => 'registry.instrument.edit',
    ],
    'Detach' => 'registry.detach',
    'Stamp' => 'registry.stamp',
    'Check' => 'registry.check',
    'Execute' => 'registry.execute',
    'Dispatch' => 'registry.dispatch',
    'Reset' => [
      'Detachment' => 'registry.reset.detach',
      'Stamping' => 'registry.reset.stamping',
      'Checking' => 'registry.reset.checking',
    ],
  ],
  'printdeeds' => [
    'Print Draft' => 'printdeeds.printDraft',
    'Print Stamp' => 'printdeeds.printStamp',
    'Final' => 'printdeeds.final',
    'CTC' => 'printdeeds.ctc',
    'Re-Print' => 'printdeeds.reprint',
    'Reset' => [
      'Print Draft' => 'printdeeds.reset.printDraft',
      'Print Stamp' => 'printdeeds.reset.printStamp',
    ],
  ],
  'plotholder' => [
    'View Plot Holders' => 'plotholder.view',
    'Notify Plot Holders' => 'plotholder.notify',
    'Edit Cofo' => 'plotholder.editCofo',
    'View Others' => 'plotholder.viewOthers',
    'Edit Records' => 'plotholder.editRecords',
  ],
  'setup' => [
    'Setup Parameters' => 'setup.parameters',
    'Access Management' => 'setup.access',
    'Print Format' => 'setup.printFormat',
    'Logs' => 'setup.logs',
    'Manual Backup' => 'setup.manualBackup',
    'Instrument View' => 'setup.instrumentView',
  ],
];

// ===== Helpers =====
function flatten_permissions(array $schema, array $acc = []): array {
  foreach ($schema as $label => $keyOrChildren) {
    if (is_array($keyOrChildren)) {
      $acc = flatten_permissions($keyOrChildren, $acc);
    } else {
      $acc[] = $keyOrChildren;
    }
  }
  return $acc;
}

function set_nested(array &$arr, array $path, bool $value): void {
  $ref =& $arr;
  foreach ($path as $p) {
    if (!isset($ref[$p]) || !is_array($ref[$p])) $ref[$p] = [];
    $ref =& $ref[$p];
  }
  $ref = $value;
}

function keys_to_nested(array $keys): array {
  $out = [];
  foreach ($keys as $key) {
    $parts = explode('.', $key);
    set_nested($out, $parts, true);
  }
  return $out;
}

function nested_to_keys(array $nested, string $prefix = ''): array {
  $keys = [];
  foreach ($nested as $k => $v) {
    $cur = $prefix === '' ? $k : $prefix . '.' . $k;
    if (is_array($v)) {
      $keys = array_merge($keys, nested_to_keys($v, $cur));
    } else {
      if ($v) $keys[] = $cur;
    }
  }
  return $keys;
}

function json_decode_safely(?string $json): array {
  if (!$json) return [];
  $data = json_decode($json, true);
  return is_array($data) ? $data : [];
}

// ===== Actions (Ajax + REST-ish) =====
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$action = $_REQUEST['action'] ?? '';

if ($method === 'POST') {
  if (!isset($_POST['csrf']) || !hash_equals($_SESSION['csrf'], (string)$_POST['csrf'])) {
    http_response_code(403);
    echo json_encode(['ok' => false, 'error' => 'CSRF failed']);
    exit;
  }

  if ($action === 'create_role') {
    $name = trim((string)($_POST['rolename'] ?? ''));
    $checked = $_POST['checked'] ?? [];
    if ($name === '') {
      echo json_encode(['ok' => false, 'error' => 'Role name is required']);
      exit;
    }
    $nested = keys_to_nested(array_map('strval', $checked));
    $json = json_encode($nested, JSON_UNESCAPED_SLASHES);

    try {
      $stmt = $pdo->prepare('INSERT INTO roles (rolename, roledesc) VALUES (:n, :d)');
      $stmt->execute([':n' => $name, ':d' => $json]);
      echo json_encode(['ok' => true, 'role' => ['roleid' => $pdo->lastInsertId(), 'rolename' => $name, 'roledesc' => $nested]]);
    } catch (PDOException $e) {
      http_response_code(400);
      echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
    }
    exit;
  }

  if ($action === 'update_role') {
    $id = (int)($_POST['roleid'] ?? 0);
    $name = trim((string)($_POST['rolename'] ?? ''));
    $checked = $_POST['checked'] ?? [];
    if ($id <= 0) { echo json_encode(['ok'=>false,'error'=>'Invalid role id']); exit; }
    if ($name === '') { echo json_encode(['ok'=>false,'error'=>'Role name is required']); exit; }
    $nested = keys_to_nested(array_map('strval', $checked));
    $json = json_encode($nested, JSON_UNESCAPED_SLASHES);
    try {
      $stmt = $pdo->prepare('UPDATE roles SET rolename = :n, roledesc = :d WHERE roleid = :id');
      $stmt->execute([':n'=>$name, ':d'=>$json, ':id'=>$id]);
      echo json_encode(['ok'=>true]);
    } catch (PDOException $e) {
      http_response_code(400);
      echo json_encode(['ok'=>false,'error'=>$e->getMessage()]);
    }
    exit;
  }

  if ($action === 'delete_role') {
    $id = (int)($_POST['roleid'] ?? 0);
    if ($id <= 0) { echo json_encode(['ok'=>false,'error'=>'Invalid role id']); exit; }
    try {
      $pdo->prepare('DELETE FROM roles WHERE roleid = :id')->execute([':id'=>$id]);
      echo json_encode(['ok'=>true]);
    } catch (PDOException $e) {
      http_response_code(400);
      echo json_encode(['ok'=>false,'error'=>$e->getMessage()]);
    }
    exit;
  }
}

if ($method === 'GET' && $action === 'role_details') {
  $id = (int)($_GET['roleid'] ?? 0);
  if ($id <= 0) { echo json_encode(['ok'=>false,'error'=>'Invalid role id']); exit; }
  $row = $pdo->prepare('SELECT roleid, rolename, roledesc FROM roles WHERE roleid = :id');
  $row->execute([':id'=>$id]);
  $role = $row->fetch(PDO::FETCH_ASSOC);
  if (!$role) { echo json_encode(['ok'=>false,'error'=>'Not found']); exit; }
  echo json_encode(['ok'=>true,'role'=>$role]);
  exit;
}

$roles = $pdo->query('SELECT roleid, rolename, roledesc FROM roles ORDER BY rolename')->fetchAll(PDO::FETCH_ASSOC);
$ALL_KEYS = flatten_permissions($SCHEMA);
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Roles & Permissions</title>
  <link rel="stylesheet" href="../css/view_css2.css" />
  <style>
    :root{
      --bg:#f6f7fb; --card:#fff; --muted:#6b7280; --bdr:#e6e6e6; --brand:#0b57d0; --danger:#b3261e; --ok:#17a34a;
    }
    *{box-sizing:border-box}
    body{margin:0;padding:16px;font-family:Segoe UI, Tahoma, Arial, sans-serif;background:var(--bg);} 
    h1{margin:10px 0 16px;font-size:20px}
    .toolbar{display:flex;gap:8px;align-items:center;margin:8px 0 16px}
    .btn{padding:8px 12px;border:1px solid #ccc;border-radius:10px;background:#fff;cursor:pointer}
    .btn.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
    .btn.danger{background:var(--danger);color:#fff;border-color:var(--danger)}
    .btn.ghost{background:transparent}
    .btn[disabled]{opacity:.6;cursor:not-allowed}
    .grid{display:grid;grid-template-columns:320px 1fr;gap:16px}
    .card{background:var(--card);border:1px solid var(--bdr);border-radius:14px;box-shadow:0 1px 2px rgba(0,0,0,.05)}
    .card .hd{padding:12px 14px;border-bottom:1px solid #eee;font-weight:600;display:flex;align-items:center;gap:8px}
    .card .bd{padding:14px;max-height:70vh;overflow:auto}

    .roles-list table{width:100%;border-collapse:collapse}
    .roles-list th, .roles-list td{padding:10px;border-bottom:1px solid #f0f0f0;font-size:14px}
    .roles-list tr:hover{background:#f9fbff}

    /* Modals */
    .modal-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.35);display:none;align-items:center;justify-content:center;z-index:999}
    .modal{width:min(960px,96vw);max-height:90vh;overflow:auto;background:#fff;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,.35)}
    .modal-hd{position:sticky;top:0;background:#fff;border-bottom:1px solid #eee;padding:12px 14px;display:flex;align-items:center;gap:8px}
    .modal-tt{font-weight:700;font-size:16px}
    .modal-bd{padding:14px}
    .modal-ft{position:sticky;bottom:0;background:#fff;border-top:1px solid #eee;padding:12px 14px;display:flex;gap:8px;justify-content:flex-end}

    /* Permission UI */
    .perm-section{border:1px solid #e9e9e9;border-radius:12px;margin:12px 0;overflow:hidden}
    .perm-title{background:#f4f6fb;border-bottom:1px solid #e9e9e9;padding:10px 12px;font-weight:600;display:flex;align-items:center;gap:10px}
    .perm-title .right{margin-left:auto;font-weight:500;color:var(--muted);display:flex;align-items:center;gap:12px}
    .perm-items{padding:6px 10px}
    .perm-items .row{display:flex;align-items:center;gap:10px;padding:8px 6px;border-bottom:1px dashed #f0f0f0}
    .perm-items .row:last-child{border-bottom:none}
    .perm-sub{margin-left:28px;border-left:3px solid #eef2ff;padding-left:10px;margin-top:6px}
    .group-toggle{display:flex;align-items:center;gap:8px}

    /* Toast */
    .toast{position:fixed;top:12px;left:50%;transform:translateX(-50%);background:var(--brand);color:#fff;padding:10px 14px;border-radius:999px;box-shadow:0 6px 18px rgba(0,0,0,.18);display:none;z-index:1000}

    .muted{color:var(--muted)}
    .field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
    input[type="text"], select{padding:9px 11px;border:1px solid #ccc;border-radius:10px;background:#fff}
    label.check{display:inline-flex;align-items:center;gap:8px;cursor:pointer}
    .badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid #e5e7eb}
    .badge.ok{background:#ecfdf5;color:#065f46;border-color:#a7f3d0}
    .badge.off{background:#f9fafb;color:#6b7280}

    /* Empty state */
    .empty{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:220px;text-align:center;color:var(--muted);gap:10px}
    .empty svg{width:72px;height:72px}

    /* View layout */
    .kv{display:grid;grid-template-columns:160px 1fr;gap:8px;margin-bottom:12px}
    .chips{display:flex;flex-wrap:wrap;gap:6px}
    .chip{padding:6px 10px;border-radius:999px;background:#eef2ff;border:1px solid #e0e7ff;font-size:12px}
  </style>
</head>
<body>
  <h1>Roles & Permissions</h1>
  <div class="toolbar">
    <button class="btn primary" id="btnAdd">+ Add Role</button>
    <button class="btn" id="btnEdit" disabled>Edit Selected</button>
    <button class="btn danger" id="btnDelete" disabled>Delete Selected</button>
  </div>

  <div class="grid">
    <div class="card roles-list">
      <div class="hd">All Roles</div>
      <div class="bd">
        <table id="rolesTable">
          <thead>
            <tr><th style="width:36px"></th><th>Role Name</th><th style="width:100px">Actions</th></tr>
          </thead>
          <tbody>
          <?php foreach ($roles as $r): ?>
            <tr data-id="<?= (int)$r['roleid'] ?>" data-name="<?= htmlspecialchars($r['rolename']) ?>">
              <td><input type="radio" name="rolepick" /></td>
              <td><?= htmlspecialchars($r['rolename']) ?></td>
              <td>
                <button class="btn btn-sm" data-act="view">View</button>
              </td>
            </tr>
          <?php endforeach; ?>
          </tbody>
        </table>
      </div>
    </div>

    <div class="card" id="detailsCard">
      <div class="hd">Role Details</div>
      <div class="bd" id="detailsBody">
        <div class="empty" id="emptyState">
          <!-- cute shield/lock icon -->
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 3l7 3v6c0 5-3.5 9-7 9s-7-4-7-9V6l7-3z"/>
            <path d="M9.5 12.5a2.5 2.5 0 115 0v2a1.5 1.5 0 01-1.5 1.5h-2A1.5 1.5 0 019.5 14.5v-2z"/>
          </svg>
          <div style="font-weight:600;color:#111827">No role selected</div>
          <div>Choose a role on the left to preview its permissions, or click <em>Add Role</em> to create one.</div>
        </div>
      </div>
    </div>
  </div>

  <!-- Create/Edit Modal -->
  <div class="modal-backdrop" id="modalBackdrop" role="dialog" aria-modal="true" aria-hidden="true">
    <div class="modal" role="document">
      <div class="modal-hd">
        <div class="modal-tt" id="modalTitle">Add Role</div>
        <div style="margin-left:auto"></div>
        <button class="btn" id="btnCloseModal">Close</button>
      </div>
      <form id="roleForm">
        <input type="hidden" name="csrf" value="<?= htmlspecialchars($CSRF) ?>" />
        <input type="hidden" name="action" value="create_role" />
        <input type="hidden" name="roleid" value="" />
        <div class="modal-bd">
          <div class="field">
            <label>Role name</label>
            <input type="text" name="rolename" placeholder="e.g. Data Capture Officer" required />
          </div>

          <?php
          function render_section($title, $block, $prefix = '') {
            $sectionKey = strtolower(preg_replace('/\s+Tab$/','',$title));
            echo '<div class="perm-section" data-section="'.htmlspecialchars($sectionKey).'">';
            echo '<div class="perm-title">';
            echo '<div class="group-toggle"><input type="checkbox" class="section-toggle" data-scope="section"> <span>' . htmlspecialchars($title) . '</span></div>';
            echo '<div class="right"><button type="button" class="btn ghost small select-all" data-act="select">Select all</button><button type="button" class="btn ghost small deselect-all" data-act="deselect">Deselect all</button></div>';
            echo '</div>';
            echo '<div class="perm-items">';
            foreach ($block as $label => $keyOrChildren) {
              if (is_array($keyOrChildren)) {
                $gid = 'g_' . md5($label.$sectionKey);
                echo '<div class="row" data-group="'.htmlspecialchars($gid).'">';
                echo '  <label class="check"><input type="checkbox" class="group-toggle-cb" data-scope="group"> <strong>' . htmlspecialchars($label) . '</strong></label>';
                echo '</div>';
                echo '<div class="perm-sub" data-parent="'.htmlspecialchars($gid).'">';
                foreach ($keyOrChildren as $sublabel => $subkey) {
                  if (is_array($subkey)) {
                    foreach ($subkey as $sslabel => $sskey) {
                      $id = 'p_' . md5($sskey);
                      echo '<div class="row"><label class="check"><input type="checkbox" name="checked[]" value="' . htmlspecialchars($sskey) . '" data-key="' . htmlspecialchars($sskey) . '" id="' . $id . '"><span>' . htmlspecialchars($sslabel) . '</span></label></div>';
                    }
                  } else {
                    $id = 'p_' . md5($subkey);
                    echo '<div class="row"><label class="check"><input type="checkbox" name="checked[]" value="' . htmlspecialchars($subkey) . '" data-key="' . htmlspecialchars($subkey) . '" id="' . $id . '"><span>' . htmlspecialchars($sublabel) . '</span></label></div>';
                  }
                }
                echo '</div>';
              } else {
                $id = 'p_' . md5($keyOrChildren);
                echo '<div class="row"><label class="check"><input type="checkbox" name="checked[]" value="' . htmlspecialchars($keyOrChildren) . '" data-key="' . htmlspecialchars($keyOrChildren) . '" id="' . $id . '"><span>' . htmlspecialchars($label) . '</span></label></div>';
              }
            }
            echo '</div></div>';
          }

          foreach ($SCHEMA as $sectionTitle => $block) {
            render_section(ucfirst($sectionTitle) . ' Tab', $block);
          }
          ?>
        </div>
        <div class="modal-ft">
          <button type="button" class="btn" id="btnCancel">Cancel</button>
          <button type="submit" class="btn primary" id="btnSave">Save</button>
        </div>
      </form>
    </div>
  </div>

  <!-- Delete Modal -->
  <div class="modal-backdrop" id="deleteBackdrop" role="dialog" aria-modal="true" aria-hidden="true">
    <div class="modal" role="document" style="max-width:520px">
      <div class="modal-hd">
        <div class="modal-tt">Delete Role</div>
        <div style="margin-left:auto"></div>
        <button class="btn" id="btnCloseDelete">Close</button>
      </div>
      <div class="modal-bd">
        <div style="display:flex;gap:12px;align-items:flex-start">
          <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg>
          <div>
            <div style="font-weight:700;margin-bottom:4px">Are you sure?</div>
            <div class="muted">This will permanently remove <span id="delRoleName" style="font-weight:600;color:#111827"></span> and all its permissions.</div>
          </div>
        </div>
      </div>
      <div class="modal-ft">
        <button class="btn" id="btnCancelDelete">Cancel</button>
        <button class="btn danger" id="btnConfirmDelete">Delete</button>
      </div>
    </div>
  </div>

  <div class="toast" id="toast"></div>

<script>
(function(){
  const $ = (s,root=document)=>root.querySelector(s);
  const $$ = (s,root=document)=>Array.from(root.querySelectorAll(s));
  const modal = $('#modalBackdrop');
  const roleForm = $('#roleForm');
  const modalTitle = $('#modalTitle');
  const btnAdd = $('#btnAdd');
  const btnEdit = $('#btnEdit');
  const btnDelete = $('#btnDelete');
  const toast = $('#toast');
  const rolesTable = $('#rolesTable tbody');
  const detailsBody = $('#detailsBody');

  // Delete modal elements
  const deleteModal = $('#deleteBackdrop');
  const btnCloseDelete = $('#btnCloseDelete');
  const btnCancelDelete = $('#btnCancelDelete');
  const btnConfirmDelete = $('#btnConfirmDelete');
  const delRoleName = $('#delRoleName');
  let deleteTargetId = null;

  function showToast(msg, ok=true){
    toast.textContent = msg;
    toast.style.background = ok ? '#0b57d0' : '#b3261e';
    toast.style.display = 'block';
    setTimeout(()=>toast.style.display='none', 2300);
  }

  function openModal(mode='create', payload=null){
    modalTitle.textContent = (mode === 'edit') ? 'Edit Role' : 'Add Role';
    roleForm.action.value = (mode === 'edit') ? 'update_role' : 'create_role';
    roleForm.reset();
    // Uncheck all
    $$("input[type='checkbox'][name='checked[]']", roleForm).forEach(cb=>cb.checked=false);
    // Reset group & section toggles
    $$('.group-toggle-cb', roleForm).forEach(cb=>{cb.checked=false;cb.indeterminate=false});
    $$('.section-toggle', roleForm).forEach(cb=>{cb.checked=false;cb.indeterminate=false});

    if(payload){
      roleForm.roleid.value = payload.roleid || '';
      roleForm.rolename.value = payload.rolename || '';
      if (payload.roledesc) {
        try {
          const keys = nestedToKeys(JSON.parse(payload.roledesc));
          const set = new Set(keys);
          $$("input[type='checkbox'][name='checked[]']", roleForm).forEach(cb=>{
            cb.checked = set.has(cb.value);
          });
          // After restoring children, compute group/section states
          updateAllGroupStates();
          updateAllSectionStates();
        } catch(e){}
      }
    }
    modal.style.display = 'flex';
  }
  function closeModal(){ modal.style.display = 'none'; }

  function openDeleteDialog(id, name){
    deleteTargetId = id;
    delRoleName.textContent = name || 'this role';
    deleteModal.style.display = 'flex';
  }
  function closeDeleteDialog(){ deleteTargetId = null; deleteModal.style.display='none'; }

  function getSelectedRoleRow(){
    const row = rolesTable.querySelector('tr input[type=radio]:checked');
    return row ? row.closest('tr') : null;
  }

  function renderRoleDetails(role){
    const nested = safeParse(role.roledesc);
    const keys = new Set(nestedToKeys(nested));

    // header
    const top = document.createElement('div');
    top.className = 'kv';
    top.innerHTML = `<div class="muted">Role name</div><div style="font-weight:700">${escapeHtml(role.rolename)}</div>`;

    // chips of enabled permissions
    const enabled = Array.from(keys).sort();
    const chipsWrap = document.createElement('div');
    chipsWrap.innerHTML = '<div class="muted" style="margin-bottom:6px">Enabled permissions</div>';
    const chips = document.createElement('div');
    chips.className = 'chips';
    if (enabled.length){
      enabled.forEach(k=>{
        const span = document.createElement('span');
        span.className='chip';
        span.textContent = k;
        chips.appendChild(span);
      });
    } else {
      const span = document.createElement('span');
      span.className='muted';
      span.textContent='No permissions set';
      chips.appendChild(span);
    }
    chipsWrap.appendChild(chips);

    // section table-like view showing on/off quickly
    const table = document.createElement('div');
    table.style.marginTop = '12px';

    const sections = <?php echo json_encode($SCHEMA); ?>;
    for (const [sectionTitle, block] of Object.entries(sections)){
      const cont = document.createElement('div');
      cont.className='perm-section';
      const hd = document.createElement('div');
      hd.className='perm-title';
      hd.innerHTML = `<span>${sectionTitle.charAt(0).toUpperCase()+sectionTitle.slice(1)} Tab</span>`;
      const bd = document.createElement('div');
      bd.className='perm-items';

      renderViewRows(bd, block, keys);
      cont.appendChild(hd); cont.appendChild(bd);
      table.appendChild(cont);
    }

    detailsBody.innerHTML='';
    detailsBody.appendChild(top);
    detailsBody.appendChild(chipsWrap);
    detailsBody.appendChild(table);
  }

  function renderViewRows(root, block, keys){
    for (const [label, val] of Object.entries(block)){
      if (typeof val === 'string'){
        const on = keys.has(val);
        const row = document.createElement('div');
        row.className='row';
        row.innerHTML = `<span style="min-width:14px">${on ? '✅' : '⬜'}</span><span>${escapeHtml(label)}</span><span class="badge ${on?'ok':'off'}" style="margin-left:auto">${on?'On':'Off'}</span>`;
        root.appendChild(row);
      } else {
        const row = document.createElement('div');
        row.className='row';
        row.innerHTML = `<strong>${escapeHtml(label)}</strong>`;
        root.appendChild(row);
        const sub = document.createElement('div');
        sub.className='perm-sub';
        renderViewRows(sub, val, keys);
        root.appendChild(sub);
      }
    }
  }

  function escapeHtml(s){ return (s+'').replace(/[&<>\"]/g, c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c])); }
  function safeParse(s){ try { const x = JSON.parse(s||'{}'); return (x && typeof x==='object')?x:{}; } catch(e){ return {}; } }
  function nestedToKeys(nested, prefix=''){
    const keys = [];
    for (const k in nested){
      const v = nested[k];
      const cur = prefix ? prefix + '.' + k : k;
      if (v && typeof v === 'object' && !Array.isArray(v)){
        keys.push(...nestedToKeys(v, cur));
      } else if (v){
        keys.push(cur);
      }
    }
    return keys;
  }

  // ===== Group & Section toggle logic =====
  function updateGroupState(groupCb){
    const row = groupCb.closest('[data-group]');
    if(!row) return;
    const gid = row.getAttribute('data-group');
    const children = $$(`[data-parent="${CSS.escape(gid)}"] input[type=checkbox][name='checked[]']`, roleForm);
    const total = children.length;
    const checked = children.filter(c=>c.checked).length;
    groupCb.indeterminate = checked>0 && checked<total;
    groupCb.checked = checked===total && total>0;
  }
  function updateAllGroupStates(){ $$('.group-toggle-cb', roleForm).forEach(updateGroupState); }

  function updateSectionState(sectionCb){
    const section = sectionCb.closest('.perm-section');
    const children = $$("input[type=checkbox][name='checked[]']", section);
    const total = children.length;
    const checked = children.filter(c=>c.checked).length;
    sectionCb.indeterminate = checked>0 && checked<total;
    sectionCb.checked = checked===total && total>0;
  }
  function updateAllSectionStates(){ $$('.section-toggle', roleForm).forEach(updateSectionState); }

  roleForm.addEventListener('change', (e)=>{
    const t = e.target;
    if (t.matches('.group-toggle-cb')){
      // toggle all children in group
      const gid = t.closest('[data-group]').getAttribute('data-group');
      const kids = $$(`[data-parent=\"${CSS.escape(gid)}\"] input[type=checkbox][name='checked[]']`, roleForm);
      kids.forEach(k=>k.checked = t.checked);
      // after change, update section state too
      const sectionCb = t.closest('.perm-section').querySelector('.section-toggle');
      updateGroupState(t); updateSectionState(sectionCb);
    } else if (t.matches('.section-toggle')){
      const section = t.closest('.perm-section');
      const kids = $$("input[type=checkbox][name='checked[]'], .group-toggle-cb", section);
      kids.forEach(k=>{ if(k.name==='checked[]' || k.classList.contains('group-toggle-cb')) k.checked = t.checked; });
      // clean indeterminate
      $$('.group-toggle-cb', section).forEach(cb=>cb.indeterminate=false);
      t.indeterminate=false;
    } else if (t.matches("input[type=checkbox][name='checked[]']")){
      // child changed -> update group and section
      const groupRow = t.closest('[data-parent]');
      if (groupRow){
        const gid = groupRow.getAttribute('data-parent');
        const groupCb = $(`[data-group=\"${CSS.escape(gid)}\"] .group-toggle-cb`, roleForm);
        if (groupCb) updateGroupState(groupCb);
      }
      const sectionCb = t.closest('.perm-section').querySelector('.section-toggle');
      if (sectionCb) updateSectionState(sectionCb);
    }
  });

  // select/deselect all buttons in section header
  roleForm.addEventListener('click', (e)=>{
    const btn = e.target.closest('.select-all, .deselect-all');
    if(!btn) return;
    const section = e.target.closest('.perm-section');
    const checked = btn.classList.contains('select-all');
    $$("input[type=checkbox][name='checked[]']", section).forEach(cb=>cb.checked=checked);
    $$('.group-toggle-cb', section).forEach(cb=>{cb.checked=checked;cb.indeterminate=false});
    const sectionCb = section.querySelector('.section-toggle');
    sectionCb.checked=checked; sectionCb.indeterminate=false;
  });

  // Handlers
  $('#btnCloseModal').addEventListener('click', closeModal);
  $('#btnCancel').addEventListener('click', closeModal);
  modal.addEventListener('click', (e)=>{ if(e.target===modal) closeModal(); });

  btnAdd.addEventListener('click', ()=>openModal('create'));

  rolesTable.addEventListener('change', ()=>{
    const row = getSelectedRoleRow();
    btnEdit.disabled = !row; btnDelete.disabled = !row;
  });

  rolesTable.addEventListener('click', (e)=>{
    const actBtn = e.target.closest('button[data-act]');
    if(!actBtn) return;
    const row = e.target.closest('tr');
    const id = row?.dataset.id;
    if(!id) return;

    fetch('?action=role_details&roleid=' + encodeURIComponent(id))
      .then(r=>r.json())
      .then(j=>{
        if(j.ok){ renderRoleDetails(j.role); }
        else showToast(j.error||'Error', false);
      })
      .catch(()=>showToast('Network error', false));
  });

  btnEdit.addEventListener('click', ()=>{
    const row = getSelectedRoleRow(); if(!row) return;
    const id = row.dataset.id;
    fetch('?action=role_details&roleid=' + encodeURIComponent(id))
      .then(r=>r.json())
      .then(j=>{
        if(!j.ok) return showToast(j.error||'Error', false);
        openModal('edit', j.role);
      })
      .catch(()=>showToast('Network error', false));
  });

  // Delete now uses custom dialog
  btnDelete.addEventListener('click', ()=>{
    const row = getSelectedRoleRow(); if(!row) return;
    const id = row.dataset.id; const name = row.dataset.name;
    openDeleteDialog(id, name);
  });
  btnCloseDelete.addEventListener('click', closeDeleteDialog);
  btnCancelDelete.addEventListener('click', closeDeleteDialog);
  deleteModal.addEventListener('click', (e)=>{ if(e.target===deleteModal) closeDeleteDialog(); });

  btnConfirmDelete.addEventListener('click', ()=>{
    if(!deleteTargetId) return;
    const form = new FormData();
    form.append('csrf', '<?= htmlspecialchars($CSRF) ?>');
    form.append('action', 'delete_role');
    form.append('roleid', deleteTargetId);
    fetch('', {method:'POST', body: form})
      .then(r=>r.json())
      .then(j=>{
        if(j.ok){
          const row = rolesTable.querySelector(`tr[data-id="${CSS.escape(String(deleteTargetId))}"]`);
          if (row) row.remove();
          detailsBody.innerHTML = document.querySelector('#emptyState').outerHTML;
          showToast('Role deleted');
          btnEdit.disabled = btnDelete.disabled = true;
          closeDeleteDialog();
        } else showToast(j.error||'Error', false);
      })
      .catch(()=>showToast('Network error', false));
  });

  roleForm.addEventListener('submit', (e)=>{
    e.preventDefault();
    const fd = new FormData(roleForm);
    fetch('', { method:'POST', body: fd })
      .then(r=>r.json())
      .then(j=>{
        if(!j.ok) return showToast(j.error||'Error', false);
        if (roleForm.action.value === 'create_role' && j.role){
          const tr = document.createElement('tr');
          tr.dataset.id = j.role.roleid;
          tr.dataset.name = j.role.rolename;
          tr.innerHTML = '<td><input type="radio" name="rolepick" /></td>' +
                         '<td>'+escapeHtml(j.role.rolename)+'</td>' +
                         '<td><button class="btn btn-sm" data-act="view">View</button></td>';
          rolesTable.appendChild(tr);
        }
        showToast(roleForm.action.value==='create_role'?'Role created':'Role updated');
        closeModal();
      })
      .catch(()=>showToast('Network error', false));
  });

  // Row click selects radio
$('#rolesTable').addEventListener('click',e=>{
const row=e.target.closest('tr');
if(!row) return;
// mark selected
$$('#rolesTable tr').forEach(r=>r.classList.remove('selected'));
row.classList.add('selected');
row.querySelector('input[type=radio]').checked=true;
selectedRole={roleid:row.dataset.id,rolename:row.dataset.name,roledesc:row.dataset.desc||'{}'};
$('#btnEdit').disabled=false;
$('#btnDelete').disabled=false;
// if clicked on view button
if(e.target.matches('button[data-act=view]')){
renderDetails(selectedRole);
}
});
})();
</script>
</body>
</html>
