<?php
/**
 * @intranet-file base/roles/index.php
 * @intranet-file-version 2026.05.06.1
 * @intranet-file-date 2026-05-06
 * @intranet-file-owner CSIF Intranet
 */
declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/common/app_guard.php';
require_once dirname(__DIR__, 2) . '/common/url.php';
require_once dirname(__DIR__, 2) . '/common/roles.php';
checkAppAccess();

require_once dirname(__DIR__, 2) . '/common/catalogo_db.php';

if (!catalogo_is_admin()) {
    http_response_code(403);
    die('Acceso restringido');
}

$pdo = db();
intranet_roles_ensure_schema($pdo);

function h($s): string
{
    return htmlspecialchars((string) $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function roles_redirect(int $ok): void
{
    header('Location: index.php?ok=' . $ok . '&ts=' . time());
    exit;
}

$msg = '';
$err = '';

if (isset($_GET['ok'])) {
    $ok = (int) $_GET['ok'];
    if ($ok === 1) $msg = 'Rol creado correctamente.';
    if ($ok === 2) $msg = 'Cambios guardados.';
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        catalogo_csrf_validate($_POST['csrf'] ?? null, 'roles_admin_csrf');
        $action = trim((string) ($_POST['action'] ?? ''));

        if ($action === 'add') {
            $roleKey = intranet_role_key_normalize((string) ($_POST['role_key'] ?? ''));
            $label = trim((string) ($_POST['label'] ?? ''));
            $description = trim((string) ($_POST['description'] ?? ''));
            $isEnabled = isset($_POST['is_enabled']) && (string) $_POST['is_enabled'] === '1' ? 1 : 0;

            if ($roleKey === 'administradores') {
                throw new RuntimeException('El rol administradores es de sistema y ya se mantiene automaticamente.');
            }
            if ($roleKey === '' || !intranet_role_key_is_valid($roleKey)) {
                throw new RuntimeException('Identificador invalido. Debe ser un grupo de seguridad AD con prefijo app_.');
            }
            if ($label === '') {
                $label = intranet_role_label_from_key($roleKey);
            }

            $next = 1;
            try {
                $next = (int) ($pdo->query('SELECT COALESCE(MAX(position), 0) + 1 FROM intranet_roles')->fetchColumn() ?: 1);
            } catch (Throwable $e) {
                $next = 1;
            }

            $st = $pdo->prepare(
                "INSERT INTO intranet_roles (role_key, label, description, is_enabled, is_system, position)
                 VALUES (:role_key, :label, :description, :is_enabled, 0, :position)"
            );
            $st->execute([
                'role_key' => $roleKey,
                'label' => $label,
                'description' => $description !== '' ? $description : null,
                'is_enabled' => $isEnabled,
                'position' => $next,
            ]);

            roles_redirect(1);
        }

        if ($action === 'bulk_save') {
            $rows = $_POST['roles'] ?? [];
            if (!is_array($rows)) {
                $rows = [];
            }
            $usage = intranet_role_usage_counts($pdo);
            $known = [];
            foreach (intranet_roles_fetch($pdo, false) as $role) {
                $known[(string) $role['role_key']] = $role;
            }

            usort($rows, static function ($a, $b): int {
                $pa = is_array($a) ? (int) ($a['position'] ?? 0) : 0;
                $pb = is_array($b) ? (int) ($b['position'] ?? 0) : 0;
                return $pa <=> $pb;
            });

            $position = 0;
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $roleKey = intranet_role_key_normalize((string) ($row['role_key'] ?? ''));
                if ($roleKey === '' || !isset($known[$roleKey])) {
                    continue;
                }

                $isSystem = !empty($known[$roleKey]['is_system']);
                $inUse = (int) ($usage[$roleKey] ?? 0);
                $delete = !empty($row['_delete']);

                if ($delete) {
                    if ($roleKey === 'administradores' || $isSystem) {
                        throw new RuntimeException('No se puede eliminar el rol protegido "' . $roleKey . '".');
                    }
                    if ($inUse > 0) {
                        throw new RuntimeException('No se puede eliminar "' . $roleKey . '" porque esta usado por ' . $inUse . ' app(s)/plugin(es).');
                    }
                    $st = $pdo->prepare('DELETE FROM intranet_roles WHERE role_key = :role_key');
                    $st->execute(['role_key' => $roleKey]);
                    continue;
                }

                $position++;
                $update = !empty($row['_update']);
                $label = trim((string) ($row['label'] ?? ''));
                $description = trim((string) ($row['description'] ?? ''));
                $isEnabled = !empty($row['is_enabled']) ? 1 : 0;
                if ($roleKey === 'administradores') {
                    $isEnabled = 1;
                }
                if ($label === '') {
                    $label = intranet_role_label_from_key($roleKey);
                }

                if ($update) {
                    $st = $pdo->prepare(
                        "UPDATE intranet_roles
                            SET label = :label,
                                description = :description,
                                is_enabled = :is_enabled,
                                position = :position
                          WHERE role_key = :role_key"
                    );
                    $st->execute([
                        'role_key' => $roleKey,
                        'label' => $label,
                        'description' => $description !== '' ? $description : null,
                        'is_enabled' => $isEnabled,
                        'position' => $position,
                    ]);
                } else {
                    $st = $pdo->prepare('UPDATE intranet_roles SET position = :position WHERE role_key = :role_key');
                    $st->execute(['position' => $position, 'role_key' => $roleKey]);
                }
            }

            roles_redirect(2);
        }

        throw new RuntimeException('Accion no soportada.');
    } catch (Throwable $e) {
        $err = $e->getMessage();
    }
}

$roles = intranet_roles_fetch($pdo, false);
$usage = intranet_role_usage_counts($pdo);
$csrf = catalogo_csrf_token('roles_admin_csrf');
?>
<!doctype html>
<html lang="es" class="app-html" <?= intranet_theme_html_attr(); ?>>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Administracion - Roles</title>
  <?= intranet_theme_bootstrap_script_tag(); ?>
  <link rel="stylesheet" href="<?= h(asset_url('styles/css/style.css')) ?>">
  <link rel="stylesheet" href="<?= h(asset_url('styles/css/theme-vars.css')) ?>">
  <script src="<?= h(asset_url('styles/js/table_sort.js')) ?>" defer></script>
  <style>
    .roles-page { display:grid; gap:18px; }
    .roles-form-grid {
      display:grid;
      grid-template-columns:minmax(180px, 1fr) minmax(180px, 1fr) minmax(260px, 2fr) auto auto;
      gap:12px;
      align-items:end;
    }
    .roles-field { display:flex; flex-direction:column; gap:6px; }
    .roles-field label { font-size:.82rem; font-weight:800; color:var(--text-muted); text-transform:uppercase; letter-spacing:.04em; }
    .roles-help { color:var(--text-muted); font-size:.9rem; line-height:1.45; }
    .roles-toolbar { display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap:wrap; }
    .roles-toolbar .form-control { max-width:340px; min-height:40px; }
    .roles-table-wrap { border:1px solid var(--border-light); border-radius:10px; overflow:auto; }
    .roles-table .form-control { min-width:160px; }
    .roles-table textarea.form-control { min-width:260px; min-height:44px; resize:vertical; }
    .role-key {
      display:inline-flex;
      align-items:center;
      min-height:30px;
      padding:0 10px;
      border:1px solid var(--border-light);
      border-radius:999px;
      background:var(--bg-input);
      font-weight:800;
      white-space:nowrap;
    }
    .role-badge {
      display:inline-flex;
      align-items:center;
      min-height:28px;
      padding:0 10px;
      border-radius:999px;
      border:1px solid rgba(6, 123, 59, .24);
      background:rgba(6, 123, 59, .10);
      color:var(--primary);
      font-weight:800;
      white-space:nowrap;
    }
    .role-badge--off {
      border-color:rgba(220, 38, 38, .24);
      background:rgba(220, 38, 38, .10);
      color:var(--danger-color);
    }
    .role-badge--system {
      border-color:rgba(100, 116, 139, .28);
      background:rgba(100, 116, 139, .12);
      color:var(--text-main);
    }
    .roles-actions { display:flex; gap:10px; justify-content:flex-end; flex-wrap:wrap; }
    @media (max-width: 1100px) {
      .roles-form-grid { grid-template-columns:1fr 1fr; }
    }
    @media (max-width: 760px) {
      .roles-form-grid { grid-template-columns:1fr; }
      .roles-toolbar .form-control { max-width:none; width:100%; }
    }
  </style>
</head>
<body class="app-body">
<div class="app-container roles-page">
  <header class="header-top">
    <div>
      <h2 style="margin:0;">Administracion - Roles</h2>
      <p style="margin:5px 0 0 0; opacity:0.9;">Gestion centralizada de roles para apps, plugins y seguridad LDAP.</p>
    </div>
    <div class="header-actions">
      <a href="<?= h(asset_url('dashboard.php')) ?>" target="_top" class="btn-back-dashboard">Volver al inicio</a>
    </div>
  </header>

  <?php if ($msg): ?>
    <div class="alert admin-notice"><?= h($msg) ?></div>
  <?php endif; ?>
  <?php if ($err): ?>
    <div class="alert alert-error"><?= h($err) ?></div>
  <?php endif; ?>

  <section class="card">
    <div class="card-title">Crear rol</div>
    <form method="post" class="roles-form-grid">
      <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
      <input type="hidden" name="action" value="add">

      <div class="roles-field">
        <label for="roleKey">Identificador</label>
        <input id="roleKey" class="form-control" name="role_key" placeholder="app_nuevo_rol" required pattern="app_[a-zA-Z0-9_.-]{1,96}">
        <span class="roles-help">Debe coincidir con el CN del grupo AD. Salvo administradores, debe empezar por app_.</span>
      </div>

      <div class="roles-field">
        <label for="roleLabel">Etiqueta</label>
        <input id="roleLabel" class="form-control" name="label" placeholder="Nuevo rol">
        <span class="roles-help">Nombre legible en pantallas de administracion.</span>
      </div>

      <div class="roles-field">
        <label for="roleDescription">Descripcion</label>
        <input id="roleDescription" class="form-control" name="description" placeholder="Para que sirve este rol">
        <span class="roles-help">Ayuda a elegirlo en PluginBuilder y gestion de apps.</span>
      </div>

      <div class="roles-field">
        <label>Activo</label>
        <label style="display:flex;gap:8px;align-items:center;min-height:45px;">
          <input type="checkbox" name="is_enabled" value="1" checked>
          <span>Disponible</span>
        </label>
      </div>

      <div class="roles-field">
        <label style="opacity:0;">Accion</label>
        <button type="submit" class="btn-primary" style="min-height:45px;cursor:pointer;">Crear</button>
      </div>
    </form>
  </section>

  <section class="card">
    <div class="card-title">Roles registrados</div>
    <div class="roles-toolbar">
      <input id="rolesFilter" class="form-control" type="search" placeholder="Filtrar por rol, etiqueta o descripcion...">
      <span class="roles-help">Los roles desactivados no se ofrecen como nuevos permisos, pero se conservan si ya estan asignados.</span>
    </div>

    <form method="post" style="margin-top:12px;">
      <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
      <input type="hidden" name="action" value="bulk_save">

      <div class="roles-table-wrap">
        <table id="rolesTable" class="table-sortable roles-table">
          <thead>
            <tr>
              <th data-sort-type="num">Orden</th>
              <th data-sort-type="text">Rol</th>
              <th data-sort-type="text">Etiqueta</th>
              <th data-sort-type="text">Descripcion</th>
              <th data-sort-type="num">Uso</th>
              <th data-sort-type="text">Estado</th>
              <th>Actualizar</th>
              <th>Eliminar</th>
            </tr>
          </thead>
          <tbody>
          <?php if (empty($roles)): ?>
            <tr><td colspan="8" style="padding:32px;color:var(--text-muted);">No hay roles registrados.</td></tr>
          <?php else: ?>
            <?php foreach ($roles as $i => $role): ?>
              <?php
                $roleKey = (string) $role['role_key'];
                $inUse = (int) ($usage[$roleKey] ?? 0);
                $isSystem = !empty($role['is_system']);
                $isAdminRole = $roleKey === 'administradores';
              ?>
              <tr data-filter="<?= h(strtolower($roleKey . ' ' . (string) $role['label'] . ' ' . (string) ($role['description'] ?? ''))) ?>">
                <td data-sort="<?= (int) $role['position'] ?>">
                  <input type="hidden" name="roles[<?= $i ?>][role_key]" value="<?= h($roleKey) ?>">
                  <input class="form-control" type="number" name="roles[<?= $i ?>][position]" value="<?= (int) $role['position'] ?>" style="max-width:90px;">
                </td>
                <td data-sort="<?= h($roleKey) ?>">
                  <span class="role-key"><?= h($roleKey) ?></span>
                </td>
                <td data-sort="<?= h((string) $role['label']) ?>">
                  <input class="form-control" name="roles[<?= $i ?>][label]" value="<?= h((string) $role['label']) ?>">
                </td>
                <td data-sort="<?= h((string) ($role['description'] ?? '')) ?>">
                  <textarea class="form-control" name="roles[<?= $i ?>][description]"><?= h((string) ($role['description'] ?? '')) ?></textarea>
                </td>
                <td data-sort="<?= $inUse ?>">
                  <span class="role-badge"><?= $inUse ?> uso(s)</span>
                </td>
                <td data-sort="<?= !empty($role['is_enabled']) ? 'activo' : 'inactivo' ?>">
                  <div style="display:grid;gap:8px;">
                    <span class="role-badge <?= !empty($role['is_enabled']) ? '' : 'role-badge--off' ?>">
                      <?= !empty($role['is_enabled']) ? 'Activo' : 'Inactivo' ?>
                    </span>
                    <?php if ($isSystem): ?>
                      <span class="role-badge role-badge--system">Sistema</span>
                    <?php endif; ?>
                    <label style="display:flex;gap:8px;align-items:center;">
                      <input type="checkbox" name="roles[<?= $i ?>][is_enabled]" value="1" <?= !empty($role['is_enabled']) ? 'checked' : '' ?> <?= $isAdminRole ? 'disabled' : '' ?>>
                      <span>Disponible</span>
                    </label>
                  </div>
                </td>
                <td style="text-align:center;">
                  <input type="checkbox" name="roles[<?= $i ?>][_update]" value="1">
                </td>
                <td style="text-align:center;">
                  <input type="checkbox" name="roles[<?= $i ?>][_delete]" value="1" <?= ($isSystem || $inUse > 0) ? 'disabled' : '' ?>>
                </td>
              </tr>
            <?php endforeach; ?>
          <?php endif; ?>
          </tbody>
        </table>
      </div>

      <div class="roles-actions" style="margin-top:14px;">
        <a class="btn-secondary" href="<?= h(asset_url('base/gestion_enlaces/index.php?section=apps')) ?>">Gestion de aplicaciones</a>
        <a class="btn-secondary" href="<?= h(asset_url('base/plugins/index.php')) ?>">Gestion de plugins</a>
        <a class="btn-secondary" href="<?= h(asset_url('base/plugin_builder/index.php')) ?>">PluginBuilder</a>
        <button type="submit" class="btn-primary" style="cursor:pointer;">Guardar cambios</button>
      </div>

      <p class="roles-help" style="margin-top:10px;">
        Marca "Actualizar" para aplicar cambios en una fila. Los roles de sistema no se eliminan; los roles usados por apps o plugins tampoco.
      </p>
    </form>
  </section>
</div>

<script>
  const filter = document.getElementById('rolesFilter');
  const rows = Array.from(document.querySelectorAll('#rolesTable tbody tr[data-filter]'));
  function normalizeText(value) {
    return String(value || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
  }
  if (filter) {
    filter.addEventListener('input', function () {
      const q = normalizeText(filter.value);
      rows.forEach(function (row) {
        const text = normalizeText(row.getAttribute('data-filter'));
        row.style.display = !q || text.indexOf(q) !== -1 ? '' : 'none';
      });
    });
  }
</script>
</body>
</html>
