// ============================================================
// lib/permissions.jsx — FASE 16 (sessione 93) — RBAC navigazione
// ============================================================
// Visibilità delle voci di navigazione in base al ruolo. I permessi
// vivono su `role.permissions` (configurabili da Customizing → Ruoli) e
// vengono risolti per l'utente loggato come UNIONE dei suoi ruoli.
//
// Modello a NAMESPACE: ogni voce mappa su uno o più namespace (es. 'rda',
// 'project', 'config'). La voce è visibile se l'utente ha QUALSIASI
// permesso in quel namespace (es. 'rda.create' → vede la sezione RdA anche
// senza 'rda.read' esplicito). Robusto rispetto ai permessi seed disomogenei.
//
// Wildcard:
//   '*'        → tutto (superadmin)
//   'rda.*'    → tutto il namespace rda
//   '*.read'   → lettore cross-cutting: concede i namespace NON sensibili
//
// `SENSITIVE_NS` (config = Customizing + Impostazioni): richiede un permesso
// esplicito del namespace o '*' — un '*.read' generico NON basta.
//
// Enforcement: Sidebar nasconde le voci, App.jsx blocca la rotta. È gating
// UI — la blindatura degli endpoint API admin resta un capitolo separato.

// route/nav id → namespace di permesso. Voce assente → sempre visibile
// (dashboard, aichat, alerts). `alerts` NON è gated: è il centro attività
// personale (cosa devi firmare/approvare) — ogni utente autenticato deve
// poterlo vedere, altrimenti i task di processo restano invisibili.
const ROUTE_NS = {
  projects: ['project'],
  project_detail: ['project'],
  workflow: ['project'],
  communications: ['project', 'communication'],
  rda: ['rda'],
  oda: ['po'],
  benchmark: ['rda', 'vendor'],
  vendors: ['vendor'],
  archive: ['doc'],
  documents: ['doc'],
  reports: ['report'],
  audit: ['audit'],
  customizing: ['config'],
  settings: ['config'],
};

// Namespace sensibili: non concessi dai wildcard cross-cutting ('*.read').
const SENSITIVE_NS = ['config'];

// Permessi effettivi dell'utente = unione dei `permissions` dei suoi ruoli
// (lookup in seedCustom.ROLES, per id o per code).
function effectivePermissions(user, seedCustom) {
  const roleIds = user && Array.isArray(user.roleIds) ? user.roleIds : [];
  if (roleIds.length === 0) return [];
  const roles = (seedCustom && seedCustom.ROLES) || [];
  const out = new Set();
  for (const rid of roleIds) {
    const role = roles.find((r) => r && (r.id === rid || r.code === rid));
    const perms = role && Array.isArray(role.permissions) ? role.permissions : [];
    for (const p of perms) if (typeof p === 'string' && p) out.add(p);
  }
  return Array.from(out);
}

// L'insieme di permessi `perms` dà accesso al namespace `ns`?
function canSeeNamespace(perms, ns) {
  if (!Array.isArray(perms)) return false;
  for (const p of perms) {
    if (typeof p !== 'string' || !p) continue;
    if (p === '*') return true;                              // superadmin
    if (p === ns || p.startsWith(ns + '.')) return true;     // permesso esplicito del namespace
    if (p.startsWith('*.') && SENSITIVE_NS.indexOf(ns) < 0) return true; // wildcard reader
  }
  return false;
}

// L'utente può accedere alla rotta/voce `routeId`?
// Voce non mappata → sempre accessibile (dashboard, aichat).
function canAccessRoute(routeId, user, seedCustom) {
  const nss = ROUTE_NS[routeId];
  if (!nss || nss.length === 0) return true;
  const perms = effectivePermissions(user, seedCustom);
  return nss.some((ns) => canSeeNamespace(perms, ns));
}

// ============================================================
// FASE 16 (sessione 94) — Catalogo permessi strutturato
// ============================================================
// Sorgente unica per l'editor Ruoli (Customizing → Ruoli): namespace × azioni,
// con il mapping verso le voci di menu che ogni namespace sblocca. Sostituisce
// la vecchia textarea free-text. Il modello a permesso resta `<ns>.<action>`.

// Voci di navigazione — mirror di NAV_GROUPS in Sidebar.jsx — per l'anteprima
// "menu visibile per questo ruolo".
const NAV_PREVIEW = [
  { group: 'Operations', items: [
    { id: 'dashboard', label: 'Dashboard' },
    { id: 'projects', label: 'Progetti CAPEX' },
    { id: 'workflow', label: 'Workflow CAPEX' },
    { id: 'alerts', label: 'Le mie attività' },
    { id: 'communications', label: 'Comunicazioni' },
  ] },
  { group: 'Procurement', items: [
    { id: 'rda', label: 'Richieste di Acquisto' },
    { id: 'benchmark', label: 'Benchmark offerte' },
    { id: 'vendors', label: 'Vendor management' },
  ] },
  { group: 'Knowledge', items: [
    { id: 'archive', label: 'Archivio storico' },
    { id: 'documents', label: 'Documenti & specifiche' },
    { id: 'aichat', label: 'AI Assistant' },
  ] },
  { group: 'Governance', items: [
    { id: 'reports', label: 'Reportistica' },
    { id: 'audit', label: 'Audit log' },
    { id: 'customizing', label: 'Customizing' },
    { id: 'settings', label: 'Impostazioni' },
  ] },
];

// Catalogo: ogni gruppo è un namespace di permesso. `unlocks` = voci di menu
// sbloccate (qualsiasi permesso del namespace le rende visibili). `wildcard` =
// token "tutto il namespace". `sensitive` = namespace non concesso da `*.read`.
const PERMISSION_CATALOG = [
  { key: 'project', label: 'Progetti & CAPEX', icon: 'projects',
    unlocks: ['projects', 'workflow', 'communications'], wildcard: 'project.*',
    actions: [
      { perm: 'project.read', label: 'Vedere progetti e workflow' },
      { perm: 'project.create', label: 'Creare progetti' },
      { perm: 'project.update', label: 'Modificare progetti' },
      { perm: 'project.approve', label: 'Approvare progetti / business case' },
      { perm: 'project.delete', label: 'Eliminare progetti' },
    ] },
  { key: 'rda', label: "Richieste d'Acquisto", icon: 'rda',
    unlocks: ['rda', 'benchmark'], wildcard: 'rda.*',
    actions: [
      { perm: 'rda.read', label: 'Vedere le RdA' },
      { perm: 'rda.create', label: 'Creare RdA' },
      { perm: 'rda.edit', label: 'Modificare RdA' },
      { perm: 'rda.review', label: 'Revisionare RdA' },
      { perm: 'rda.approve', label: 'Approvare RdA' },
      { perm: 'rda.reject', label: 'Rifiutare RdA' },
    ] },
  { key: 'po', label: "Ordini di Acquisto (OdA)", icon: 'rda',
    unlocks: ['oda'], wildcard: 'po.*',
    actions: [
      { perm: 'po.read', label: 'Vedere gli OdA' },
      { perm: 'po.create', label: 'Creare OdA' },
      { perm: 'po.edit', label: 'Modificare OdA' },
    ] },
  { key: 'vendor', label: 'Vendor', icon: 'vendors',
    unlocks: ['vendors', 'benchmark'], wildcard: 'vendor.*',
    actions: [
      { perm: 'vendor.read', label: 'Vedere i vendor' },
      { perm: 'vendor.create', label: 'Creare vendor' },
      { perm: 'vendor.update', label: 'Modificare vendor' },
      { perm: 'vendor.qualify', label: 'Qualificare vendor' },
    ] },
  { key: 'doc', label: 'Documenti & Archivio', icon: 'docs',
    unlocks: ['documents', 'archive'], wildcard: 'doc.*',
    actions: [
      { perm: 'doc.read', label: 'Vedere documenti e archivio' },
      { perm: 'doc.upload', label: 'Caricare documenti' },
      { perm: 'doc.delete', label: 'Eliminare documenti' },
      { perm: 'doc.waive', label: 'Approvare waiver / eccezioni alla checklist' },
    ] },
  { key: 'communication', label: 'Comunicazioni', icon: 'comm',
    unlocks: ['communications'], wildcard: 'communication.*',
    actions: [
      { perm: 'communication.read', label: 'Vedere le comunicazioni' },
      { perm: 'communication.create', label: 'Creare / inviare comunicazioni' },
      { perm: 'communication.update', label: 'Modificare / riclassificare comunicazioni' },
    ] },
  { key: 'report', label: 'Reportistica', icon: 'reports',
    unlocks: ['reports'], wildcard: 'report.*',
    actions: [
      { perm: 'report.read', label: 'Vedere la reportistica' },
    ] },
  { key: 'audit', label: 'Audit log', icon: 'audit',
    unlocks: ['audit'], wildcard: 'audit.*',
    actions: [
      { perm: 'audit.read', label: "Consultare l'audit log" },
    ] },
  { key: 'config', label: 'Configurazione (Customizing)', icon: 'sliders', sensitive: true,
    unlocks: ['customizing', 'settings'], wildcard: 'config.*',
    actions: [
      { perm: 'config.read', label: 'Accedere a Customizing e Impostazioni' },
      { perm: 'config.update', label: 'Modificare la configurazione' },
      { perm: 'config.publish', label: 'Pubblicare versioni di configurazione' },
      { perm: 'config.rollback', label: 'Ripristinare versioni precedenti' },
    ] },
  { key: 'ai', label: 'Assistente AI', icon: 'sparkle', wildcard: 'ai.*',
    actions: [
      { perm: 'ai.use', label: 'Usare il copilota AI' },
      { perm: 'ai.configure', label: 'Configurare provider e policy AI' },
    ] },
  { key: 'user', label: 'Gestione utenze & sicurezza', icon: 'settings',
    actions: [
      { perm: 'user.read', label: 'Vedere gli utenti' },
      { perm: 'user.create', label: 'Creare utenti' },
      { perm: 'user.edit', label: 'Modificare utenti' },
      { perm: 'user.suspend', label: 'Sospendere utenti' },
      { perm: 'user.delete', label: 'Eliminare utenti' },
      { perm: 'role.assign', label: 'Assegnare ruoli' },
      { perm: 'mfa.reset', label: 'Reset MFA / password' },
      { perm: 'session.revoke', label: 'Revocare sessioni utente' },
    ] },
  { key: 'process', label: 'Processi & approvazioni', icon: 'workflow',
    actions: [
      { perm: 'sal.read', label: 'Vedere i SAL' },
      { perm: 'sal.review', label: 'Revisionare SAL' },
      { perm: 'sal.approve', label: 'Approvare SAL' },
      { perm: 'sal.pay', label: 'Liquidare SAL' },
      { perm: 'budget.read', label: 'Vedere i budget' },
      { perm: 'contract.read', label: 'Vedere i contratti' },
      { perm: 'contract.approve', label: 'Approvare contratti' },
      { perm: 'capex.approve', label: 'Approvare CAPEX' },
      { perm: 'fat.approve', label: 'Approvare FAT' },
      { perm: 'sat.approve', label: 'Approvare SAT' },
      { perm: 'specification.approve', label: 'Approvare specifiche tecniche' },
    ] },
];

// Insieme dei token modellati dal catalogo — usato per individuare i permessi
// "avanzati" non in catalogo, che l'editor preserva come chip senza perderli.
const CATALOG_KNOWN_PERMS = (() => {
  const s = new Set();
  for (const g of PERMISSION_CATALOG) {
    if (g.wildcard) s.add(g.wildcard);
    for (const a of g.actions) s.add(a.perm);
  }
  return s;
})();

// Anteprima navigazione: per ogni voce di menu, è visibile col set `perms`?
function navVisibilityForPerms(perms) {
  const out = [];
  for (const grp of NAV_PREVIEW) {
    for (const it of grp.items) {
      const nss = ROUTE_NS[it.id];
      const visible = !nss || nss.length === 0
        ? true
        : nss.some((ns) => canSeeNamespace(perms, ns));
      out.push({ id: it.id, label: it.label, group: grp.group, visible });
    }
  }
  return out;
}

// ============================================================
// FASE 16 (sessione 94) — Ruoli effettivi via deleghe
// ============================================================
// Una delega attiva (`active` + oggi nel periodo `fromDate..toDate`) fa sì che
// il sostituto erediti i ruoli del delegante. I gate FE (turno workflow, firma)
// devono usare i ruoli EFFETTIVI = ruoli propri ∪ ruoli ereditati per delega.
// Controparte FE di `lib/delegation-resolve.ts` (backend).

// La delega è attiva alla data `today` (stringa YYYY-MM-DD)?
function isDelegationActiveOn(d, today) {
  if (!d || d.active === false) return false;
  const from = d.fromDate || '';
  const to = d.toDate || '';
  if (from && today < from) return false;
  if (to && today > to) return false;
  return true;
}

// Ruoli effettivi dell'utente = roleIds propri ∪ roleIds dei deleganti con una
// delega attiva verso di lui. `delegations` da seedCustom.DELEGATIONS, `personas`
// da seed.PERSONAS. `today` opzionale (default: oggi).
function effectiveRoleIdsForUser(user, delegations, personas, today) {
  const own = user && Array.isArray(user.roleIds) ? user.roleIds : [];
  const out = new Set(own);
  const uid = user && user.id;
  if (!uid) return Array.from(out);
  const day = today || new Date().toISOString().slice(0, 10);
  const dels = Array.isArray(delegations) ? delegations : [];
  const ps = Array.isArray(personas) ? personas : [];
  for (const d of dels) {
    const toId = d && (d.toPersonaId || d.toPersona);
    if (toId !== uid || !isDelegationActiveOn(d, day)) continue;
    const fromId = d.fromPersonaId || d.fromPersona;
    const delegator = ps.find((p) => p && p.id === fromId);
    const delRoles = delegator && Array.isArray(delegator.roleIds) ? delegator.roleIds : [];
    for (const r of delRoles) if (typeof r === 'string' && r) out.add(r);
  }
  return Array.from(out);
}

// ============================================================
// FASE 2 RBAC (sessione 102) — capability gating per-azione
// ============================================================
// `can(perm, user, seedCustom)` è la controparte FE di `hasPermission` nel
// backend (lib/rbac.ts). Restituisce true se l'unione dei `role.permissions` dei
// ruoli dell'utente concede il token richiesto, considerando i wildcard:
//   '*'          → tutto
//   '<ns>.*'     → tutto il namespace
//   '*.<action>' → azione cross-namespace ESCLUSI i namespace sensibili
//
// Pattern d'uso nei componenti React (allineato al bottone Firma di s100):
//   const canCreate = window.can('project.create', user, seedCustom);
//   <Btn disabled={!canCreate} title={canCreate ? 'Nuovo' : window.whyDisabled('project.create')}>
//     Nuovo progetto
//   </Btn>
//
// L'azione finisce sempre in 403 lato backend (Fase 1 + 1b), il gating FE è
// solo UX: spiega all'utente perché il bottone è inattivo invece di lasciarlo
// cliccare e prendere un errore opaco.
function can(required, user, seedCustom) {
  if (typeof required !== 'string' || required.length === 0) return false;
  const perms = effectivePermissions(user, seedCustom);
  if (!perms.length) return false;
  const dot = required.indexOf('.');
  const reqNs = dot >= 0 ? required.slice(0, dot) : required;
  const reqAction = dot >= 0 ? required.slice(dot + 1) : '';
  for (const p of perms) {
    if (typeof p !== 'string' || !p) continue;
    if (p === '*') return true;
    if (p === required) return true;
    if (p === reqNs + '.*') return true;
    if (p.startsWith('*.')) {
      const wildAction = p.slice(2);
      if (wildAction === reqAction && SENSITIVE_NS.indexOf(reqNs) < 0) return true;
    }
  }
  return false;
}

// Etichetta amichevole per il title/tooltip del bottone disabled — pesca dal
// PERMISSION_CATALOG la label dell'azione invece di esporre il token raw.
function whyDisabled(required) {
  if (typeof required !== 'string' || !required) return 'Permesso non sufficiente';
  for (const group of PERMISSION_CATALOG) {
    const actions = (group && group.actions) || [];
    for (const a of actions) {
      if (a && a.perm === required) return 'Non hai il permesso: ' + a.label.toLowerCase();
    }
  }
  return 'Permesso richiesto: ' + required;
}

Object.assign(window, {
  ROUTE_NS, SENSITIVE_NS, effectivePermissions, canSeeNamespace, canAccessRoute,
  NAV_PREVIEW, PERMISSION_CATALOG, CATALOG_KNOWN_PERMS, navVisibilityForPerms,
  isDelegationActiveOn, effectiveRoleIdsForUser,
  can, whyDisabled,
});
