/**
 * NIS2 Agile - Common Utilities
 *
 * Funzioni condivise tra tutte le pagine del frontend.
 */

// ═══════════════════════════════════════════════════════════════════
// Notifications (Toast)
// ═══════════════════════════════════════════════════════════════════

/**
 * Mostra una notifica toast.
 * @param {string} message - Testo del messaggio
 * @param {string} type - 'success' | 'error' | 'warning' | 'info'
 * @param {number} duration - Durata in ms (default 4000)
 */
function showNotification(message, type = 'info', duration = 4000) {
    let container = document.querySelector('.notification-container');
    if (!container) {
        container = document.createElement('div');
        container.className = 'notification-container';
        document.body.appendChild(container);
    }

    const icons = {
        success: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>',
        error: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg>',
        warning: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>',
        info: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>'
    };

    const notification = document.createElement('div');
    notification.className = `notification ${type}`;
    notification.innerHTML = `
        <span class="notification-icon">${icons[type] || icons.info}</span>
        <span class="notification-message">${escapeHtml(message)}</span>
        <button class="notification-close" onclick="this.parentElement.remove()">&times;</button>
    `;

    container.appendChild(notification);

    setTimeout(() => {
        notification.classList.add('fade-out');
        setTimeout(() => notification.remove(), 300);
    }, duration);
}


// ═══════════════════════════════════════════════════════════════════
// Modal
// ═══════════════════════════════════════════════════════════════════

/**
 * Mostra un dialogo modale.
 * @param {string} title - Titolo del modale
 * @param {string} content - Contenuto HTML del body
 * @param {object} options - { footer: HTML, size: 'sm'|'md'|'lg' }
 */
function showModal(title, content, options = {}) {
    closeModal(); // Chiudi eventuale modale aperto

    const overlay = document.createElement('div');
    overlay.className = 'modal-overlay';
    overlay.id = 'modal-overlay';

    const sizeClass = options.size === 'lg' ? 'style="max-width:720px"' : options.size === 'sm' ? 'style="max-width:400px"' : '';

    overlay.innerHTML = `
        <div class="modal" ${sizeClass}>
            <div class="modal-header">
                <h3>${escapeHtml(title)}</h3>
                <button class="modal-close" onclick="closeModal()">&times;</button>
            </div>
            <div class="modal-body">${content}</div>
            ${options.footer ? `<div class="modal-footer">${options.footer}</div>` : ''}
        </div>
    `;

    // Chiudi cliccando fuori dal modale
    overlay.addEventListener('click', (e) => {
        if (e.target === overlay) closeModal();
    });

    document.body.appendChild(overlay);

    // Trigger animation
    requestAnimationFrame(() => {
        overlay.classList.add('active');
    });

    // Chiudi con ESC
    document.addEventListener('keydown', _modalEscHandler);
}

function closeModal() {
    const overlay = document.getElementById('modal-overlay');
    if (overlay) {
        overlay.classList.remove('active');
        setTimeout(() => overlay.remove(), 250);
    }
    document.removeEventListener('keydown', _modalEscHandler);
}

function _modalEscHandler(e) {
    if (e.key === 'Escape') closeModal();
}


// ═══════════════════════════════════════════════════════════════════
// Date Formatting
// ═══════════════════════════════════════════════════════════════════

/**
 * Formatta una data in formato italiano (dd/mm/yyyy).
 * @param {string|Date} date
 * @returns {string}
 */
function formatDate(date) {
    if (!date) return '-';
    const d = new Date(date);
    if (isNaN(d.getTime())) return '-';
    return d.toLocaleDateString('it-IT', {
        day: '2-digit',
        month: '2-digit',
        year: 'numeric'
    });
}

/**
 * Formatta data e ora in formato italiano.
 * @param {string|Date} date
 * @returns {string}
 */
function formatDateTime(date) {
    if (!date) return '-';
    const d = new Date(date);
    if (isNaN(d.getTime())) return '-';
    return d.toLocaleDateString('it-IT', {
        day: '2-digit',
        month: '2-digit',
        year: 'numeric',
        hour: '2-digit',
        minute: '2-digit'
    });
}

/**
 * Restituisce un'etichetta relativa (es. "2 ore fa", "ieri").
 * @param {string|Date} date
 * @returns {string}
 */
function timeAgo(date) {
    if (!date) return '';
    const d = new Date(date);
    const now = new Date();
    const diffMs = now - d;
    const diffMin = Math.floor(diffMs / 60000);
    const diffHrs = Math.floor(diffMs / 3600000);
    const diffDays = Math.floor(diffMs / 86400000);

    if (diffMin < 1) return 'Adesso';
    if (diffMin < 60) return `${diffMin} min fa`;
    if (diffHrs < 24) return `${diffHrs} ore fa`;
    if (diffDays === 1) return 'Ieri';
    if (diffDays < 7) return `${diffDays} giorni fa`;
    return formatDate(date);
}


// ═══════════════════════════════════════════════════════════════════
// Sidebar
// ═══════════════════════════════════════════════════════════════════

/**
 * Inietta la sidebar di navigazione nell'elemento #sidebar.
 */
function loadSidebar() {
    const container = document.getElementById('sidebar');
    if (!container) return;

    // Determina la pagina corrente
    const currentPage = window.location.pathname.split('/').pop() || 'dashboard.html';

    const navItems = [
        {
            label: 'Principale', i18nKey: 'nav.main',
            items: [
                { name: 'Dashboard',          href: 'dashboard.html',  icon: iconGrid(),           i18nKey: 'nav.dashboard' },
                { name: 'Compliance Journey', href: 'workflow.html',   icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/></svg>` },
                { name: 'Gap Analysis',       href: 'assessment.html', icon: iconClipboardCheck(), i18nKey: 'nav.gap_analysis' },
            ]
        },
        {
            label: 'Gestione', i18nKey: 'nav.management',
            items: [
                { name: 'Rischi',       href: 'risks.html',          icon: iconShieldExclamation(), i18nKey: 'nav.risks' },
                { name: 'Incidenti',    href: 'incidents.html',      icon: iconBell(),              i18nKey: 'nav.incidents' },
                { name: 'Policy',       href: 'policies.html',       icon: iconDocumentText(),      i18nKey: 'nav.policies' },
                { name: 'Supply Chain', href: 'supply-chain.html',   icon: iconLink(),              i18nKey: 'nav.supply_chain' },
                { name: 'Segnalazioni', href: 'whistleblowing.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/></svg>` },
                { name: 'Normative',    href: 'normative.html',      icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/></svg>` },
                { name: 'AI Cross-Analysis', href: 'cross-analysis.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"/></svg>` },
                { name: 'Knowledge Base', href: 'kb.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path d="M9 4.804A7.968 7.968 0 005.5 4c-1.255 0-2.443.29-3.5.804v10A7.969 7.969 0 015.5 14c1.669 0 3.218.51 4.5 1.385A7.962 7.962 0 0114.5 14c1.255 0 2.443.29 3.5.804v-10A7.968 7.968 0 0014.5 4c-1.255 0-2.443.29-3.5.804V12a1 1 0 11-2 0V4.804z"/></svg>` },
            ]
        },
        {
            label: 'Operativo', i18nKey: 'nav.operations',
            items: [
                { name: 'Formazione',    href: 'training.html', icon: iconAcademicCap(), i18nKey: 'nav.training' },
                { name: 'Asset',         href: 'assets.html',   icon: iconServer(),      i18nKey: 'nav.assets' },
                { name: 'Audit & Report',href: 'reports.html',  icon: iconChartBar(),    i18nKey: 'nav.audit' },
            ]
        },
        {
            label: 'Sistema', i18nKey: 'nav.system',
            items: [
                { name: 'Guida all\'uso', href: 'guida.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>` },
                { name: 'Impostazioni', href: 'settings.html',     icon: iconCog(),             i18nKey: 'nav.settings' },
                { name: 'Architettura', href: 'architecture.html', icon: iconCubeTransparent() },
                { name: 'Simulazione Demo', href: 'simulate.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd"/></svg>` },
                { name: 'Integrazioni Esterne', href: 'integrazioniext.html', icon: `<svg viewBox="0 0 20 20" fill="currentColor"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"/><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"/></svg>` },
            ]
        }
    ];

    let navHTML = '';

    // Brand
    navHTML += `
        <div class="sidebar-brand">
            <div class="sidebar-brand-icon">
                <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 2.18l7 3.12v4.7c0 4.83-3.23 9.36-7 10.57-3.77-1.21-7-5.74-7-10.57V6.3l7-3.12z"/><path d="M10 12.5l-2-2-1.41 1.41L10 15.32l5.41-5.41L14 8.5l-4 4z"/></svg>
            </div>
            <div>
                <h1>NIS2 Agile</h1>
                <span>Compliance Platform</span>
            </div>
        </div>
    `;

    // Org-switcher area (populated async for consultants)
    navHTML += `<div id="sidebar-org-switcher" style="display:none;padding:8px 12px;border-bottom:1px solid var(--gray-100);"></div>`;

    // Nav sections
    navHTML += '<nav class="sidebar-nav">';
    for (const section of navItems) {
        const sectionI18n = section.i18nKey ? ` data-i18n="${section.i18nKey}"` : '';
        navHTML += `<div class="sidebar-nav-label"${sectionI18n}>${section.label}</div>`;
        for (const item of section.items) {
            const isActive = currentPage === item.href ? 'active' : '';
            const spanI18n = item.i18nKey ? ` data-i18n="${item.i18nKey}"` : '';
            navHTML += `<a href="${item.href}" class="${isActive}">${item.icon}<span${spanI18n}>${item.name}</span></a>`;
        }
    }
    navHTML += '</nav>';

    // Footer with language toggle + user info
    navHTML += `
        <div class="sidebar-footer">
            <div class="sidebar-lang-toggle" id="sidebar-lang-toggle">
                <button class="lang-btn ${_getSavedLang() === 'it' ? 'active' : ''}" onclick="switchLang('it')" title="Italiano" aria-label="Italiano">IT</button>
                <button class="lang-btn ${_getSavedLang() === 'en' ? 'active' : ''}" onclick="switchLang('en')" title="English" aria-label="English">EN</button>
            </div>
            <div class="sidebar-user">
                <div class="sidebar-user-avatar" id="sidebar-user-avatar">--</div>
                <div class="sidebar-user-info">
                    <div class="sidebar-user-name" id="sidebar-user-name">Caricamento...</div>
                    <div class="sidebar-user-role" id="sidebar-user-role">Utente</div>
                </div>
                <button class="sidebar-logout-btn" onclick="api.logout()" title="Esci">
                    <svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 001 1h6a1 1 0 100-2H4V5h5a1 1 0 100-2H3zm11.707 3.293a1 1 0 010 1.414L12.414 10l2.293 2.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M16 10a1 1 0 00-1-1H8a1 1 0 100 2h7a1 1 0 001-1z" clip-rule="evenodd"/></svg>
                </button>
            </div>
            <div class="sidebar-version" id="sidebar-version" title="Versione applicazione"
                 style="text-align:center; padding:6px 12px; font-size:.68rem; color:var(--gray-400); cursor:pointer; border-top:1px solid var(--gray-100); margin-top:4px;"
                 onclick="_showVersionChangelog()">
                v—
            </div>
        </div>
    `;

    container.innerHTML = navHTML;

    // Carica info utente (e org-switcher se consulente)
    _loadUserInfo();

    // Mobile toggle
    _setupMobileToggle();

    // Version footer (Fase 4 / G13)
    _loadVersionFooter();

    // Firm branding white-label (Fase 5 / G16) — non bloccante
    _loadFirmBranding();

    // Impersonate banner (Fase 4 / G11 — UI)
    _renderImpersonateBanner();
}

const _roleLabels = {
    super_admin: 'Super Admin',
    org_admin: 'Admin Org',
    compliance_manager: 'Compliance Manager',
    board_member: 'Membro CDA',
    auditor: 'Auditor',
    employee: 'Utente',
    consultant: 'Consulente',
};

async function _loadUserInfo() {
    try {
        const result = await api.getMe();
        if (result.success && result.data) {
            const user = result.data;
            const nameEl = document.getElementById('sidebar-user-name');
            const avatarEl = document.getElementById('sidebar-user-avatar');
            const roleEl = document.getElementById('sidebar-user-role');
            if (nameEl) nameEl.textContent = user.full_name || user.email;
            if (avatarEl) {
                const initials = (user.full_name || user.email || '--')
                    .split(' ')
                    .map(w => w[0])
                    .slice(0, 2)
                    .join('')
                    .toUpperCase();
                avatarEl.textContent = initials;
            }
            if (roleEl) roleEl.textContent = _roleLabels[user.role] || user.role || 'Utente';

            // Save role to localStorage (ensures isConsultant() works across pages)
            if (user.role) api.setUserRole(user.role);

            // Tenant switcher: visibile per consulenti + per chiunque abbia ≥2 org
            // (Fase 3 / G10 — esposizione globale del context switch)
            _loadConsultantOrgSwitcher();
        }
    } catch (e) {
        // Silenzioso
    }
}

async function _loadConsultantOrgSwitcher() {
    try {
        const result = await api.listOrganizations();
        if (!result.success || !result.data) return;

        const orgs = result.data;
        // Mostra switcher solo se utente ha ≥2 organizzazioni
        // (single-tenant users non hanno bisogno di scegliere)
        if (orgs.length < 2) return;
        const currentOrgId = parseInt(api.orgId);
        const currentOrg = orgs.find(o => (o.id || o.organization_id) === currentOrgId);

        const switcher = document.getElementById('sidebar-org-switcher');
        if (!switcher) return;

        const dropdownId = 'org-dropdown-' + Date.now();
        switcher.style.display = 'block';
        switcher.innerHTML = `
            <div style="position:relative">
                <button onclick="document.getElementById('${dropdownId}').classList.toggle('open')"
                    style="width:100%;display:flex;align-items:center;gap:8px;padding:6px 10px;
                           background:var(--gray-50);border:1px solid var(--gray-200);border-radius:6px;
                           cursor:pointer;font-size:.8rem;font-weight:600;color:var(--gray-700);">
                    <svg width="14" height="14" viewBox="0 0 20 20" fill="currentColor" style="flex-shrink:0;color:var(--primary)">
                        <path fill-rule="evenodd" d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4zm3 1h2v2H7V5zm2 4H7v2h2V9zm2-4h2v2h-2V5zm2 4h-2v2h2V9z" clip-rule="evenodd"/>
                    </svg>
                    <span style="flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
                        ${currentOrg ? currentOrg.name : 'Seleziona azienda'}
                    </span>
                    <svg width="12" height="12" viewBox="0 0 20 20" fill="currentColor" style="flex-shrink:0">
                        <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
                    </svg>
                </button>
                <div id="${dropdownId}" class="org-switcher-dropdown"
                    style="display:none;position:absolute;left:0;right:0;top:calc(100% + 4px);
                           background:var(--card-bg);border:1px solid var(--gray-200);border-radius:6px;
                           box-shadow:var(--card-shadow);z-index:200;overflow:hidden;">
                    ${orgs.map(org => {
                        const orgId = org.id || org.organization_id;
                        const isActive = orgId === currentOrgId;
                        return `<button onclick="_switchOrg(${orgId})"
                            style="width:100%;text-align:left;padding:8px 12px;
                                   background:${isActive ? 'var(--primary-bg)' : 'transparent'};
                                   border:none;cursor:pointer;font-size:.8rem;
                                   color:${isActive ? 'var(--primary)' : 'var(--gray-700)'};
                                   font-weight:${isActive ? '600' : '400'};">
                            ${isActive ? '✓ ' : ''}${org.name}
                        </button>`;
                    }).join('')}
                    <div style="border-top:1px solid var(--gray-100)">
                        <a href="companies.html"
                           style="display:block;padding:8px 12px;font-size:.8rem;
                                  color:var(--primary);text-decoration:none;">
                            ← Tutte le aziende
                        </a>
                    </div>
                </div>
            </div>`;

        // Close on outside click
        document.addEventListener('click', (e) => {
            const dd = document.getElementById(dropdownId);
            if (dd && !switcher.contains(e.target)) dd.style.display = 'none';
        });
        // Override toggle to handle display
        const ddEl = document.getElementById(dropdownId);
        if (ddEl) {
            const btn = switcher.querySelector('button');
            if (btn) btn.onclick = () => {
                ddEl.style.display = ddEl.style.display === 'none' ? 'block' : 'none';
            };
        }
    } catch (e) {
        // Silenzioso
    }
}

async function _switchOrg(orgId) {
    // Fase 3 / G09: chiama switchContext per rotare JWT con organization_id come claim.
    // Fallback: se l'endpoint non risponde 200, applica solo il vecchio comportamento
    // (set localStorage + reload) per backward-compat con versioni precedenti.
    try {
        const result = await api.post('/auth/switchContext', { organization_id: orgId });
        if (result.success && result.data && result.data.access_token) {
            api.setTokens(result.data.access_token, result.data.refresh_token);
            api.setOrganization(orgId);
            window.location.reload();
            return;
        }
    } catch (e) {
        // continua col fallback
    }
    api.setOrganization(orgId);
    window.location.reload();
}

// ── Impersonate banner persistente (Fase 4 / G11 — UI) ──────────────────
function _renderImpersonateBanner() {
    const originToken = sessionStorage.getItem('nis2_impersonate_origin_token');
    const targetEmail = sessionStorage.getItem('nis2_impersonate_target_email');
    if (!originToken) return;

    if (document.getElementById('impersonate-banner')) return;
    const bar = document.createElement('div');
    bar.id = 'impersonate-banner';
    bar.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;background:#F59E0B;color:#1F2937;padding:8px 16px;text-align:center;font-size:.85rem;font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,.2);';
    bar.innerHTML =
        '<svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" style="vertical-align:-3px;margin-right:6px"><path d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.94 6.94a.75.75 0 011.06 0L10 6.94l.5.5L10 7.94l-.5-.5L9.5 8h-1V7l.5-.5z"/></svg>'
        + 'Modalità Impersonate — stai usando l\'account di <strong>' + (targetEmail || '?') + '</strong> &nbsp;'
        + '<button onclick="_exitImpersonate()" style="background:#1F2937;color:#fff;border:none;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:.8rem;font-weight:600;margin-left:8px;">Esci impersonate</button>';
    document.body.insertBefore(bar, document.body.firstChild);
    // Sposta il contenuto sotto
    document.body.style.paddingTop = (bar.offsetHeight + (parseInt(document.body.style.paddingTop) || 0)) + 'px';
}

function _exitImpersonate() {
    const originToken = sessionStorage.getItem('nis2_impersonate_origin_token');
    const originRefresh = sessionStorage.getItem('nis2_impersonate_origin_refresh');
    if (!originToken) return;
    api.setTokens(originToken, originRefresh || '');
    sessionStorage.removeItem('nis2_impersonate_origin_token');
    sessionStorage.removeItem('nis2_impersonate_origin_refresh');
    sessionStorage.removeItem('nis2_impersonate_target_email');
    window.location.href = '/admin/users.html';
}

// ── Firm branding white-label (Fase 5 / G16) ────────────────────────────
async function _loadFirmBranding() {
    try {
        const r = await fetch('/api/branding/current', {
            headers: api.token ? { 'Authorization': 'Bearer ' + api.token } : {}
        });
        if (!r.ok) return;
        const resp = await r.json();
        if (!resp.success || !resp.data) return;
        const b = resp.data;

        const root = document.documentElement;
        if (b.primary_color)   root.style.setProperty('--primary', b.primary_color);
        if (b.secondary_color) root.style.setProperty('--secondary', b.secondary_color);

        if (b.custom_brand_name) {
            document.querySelectorAll('.sidebar-logo-text, .auth-logo-text').forEach(function(el) {
                el.textContent = b.custom_brand_name;
            });
        }
        if (b.logo_url) {
            document.querySelectorAll('.sidebar-logo-icon img, .auth-logo-icon img').forEach(function(el) {
                el.src = b.logo_url;
            });
        }
    } catch (e) { /* silenzioso */ }
}

// ── Versioning live (Fase 4 / G13) ──────────────────────────────────────
let _versionInfo = null;
async function _loadVersionFooter() {
    try {
        const r = await fetch('/version.json?_=' + Date.now());
        if (!r.ok) return;
        _versionInfo = await r.json();
        const el = document.getElementById('sidebar-version');
        if (el && _versionInfo.version) {
            el.textContent = 'v' + _versionInfo.version + (_versionInfo.build ? ' · ' + _versionInfo.build : '');
        }
    } catch (e) { /* silenzioso */ }
}
function _showVersionChangelog() {
    if (!_versionInfo) return;
    const v = _versionInfo;
    alert('NIS2 Agile v' + v.version + '\nBuild: ' + (v.build || '—') + '\nData: ' + (v.date || '—') + '\n\n' + (v.changelog || 'Nessun changelog disponibile'));
}

function _setupMobileToggle() {
    // Crea pulsante toggle se non esiste
    if (!document.querySelector('.sidebar-toggle')) {
        const toggle = document.createElement('button');
        toggle.className = 'sidebar-toggle';
        toggle.setAttribute('aria-label', 'Apri menu');
        toggle.innerHTML = '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"/></svg>';
        toggle.addEventListener('click', () => _toggleSidebar());
        document.body.appendChild(toggle);
    }

    // Crea backdrop per mobile
    if (!document.querySelector('.sidebar-backdrop')) {
        const backdrop = document.createElement('div');
        backdrop.className = 'sidebar-backdrop';
        backdrop.addEventListener('click', () => _toggleSidebar(false));
        document.body.appendChild(backdrop);
    }
}

function _toggleSidebar(forceState) {
    const sidebar = document.getElementById('sidebar');
    const backdrop = document.querySelector('.sidebar-backdrop');
    if (!sidebar) return;

    const isOpen = typeof forceState === 'boolean' ? forceState : !sidebar.classList.contains('open');

    if (isOpen) {
        sidebar.classList.add('open');
        if (backdrop) backdrop.classList.add('visible');
    } else {
        sidebar.classList.remove('open');
        if (backdrop) backdrop.classList.remove('visible');
    }
}


// ═══════════════════════════════════════════════════════════════════
// Auth Check
// ═══════════════════════════════════════════════════════════════════

/**
 * Verifica che l'utente sia autenticato. Se non lo e', redirige al login.
 * Attiva automaticamente il timeout di sessione per inattivita'.
 * @returns {boolean}
 */
function checkAuth() {
    if (!api.isAuthenticated()) {
        window.location.href = 'login.html';
        return false;
    }
    if (!_idleInitialized) {
        _idleInitialized = true;
        initIdleTimeout();
    }
    // Inizializza FAB segnalazioni su tutte le pagine autenticate
    if (typeof initFeedbackFab === 'function') {
        initFeedbackFab();
    }
    return true;
}


// ═══════════════════════════════════════════════════════════════════
// Idle Session Timeout
// ═══════════════════════════════════════════════════════════════════

let _idleTimer = null;
let _idleWarningTimer = null;
let _idleCountdownInterval = null;
let _idleInitialized = false;

const IDLE_TIMEOUT_MS = 30 * 60 * 1000;   // 30 minuti totali
const IDLE_WARNING_MS = 5 * 60 * 1000;    // avviso 5 minuti prima della scadenza

/**
 * Avvia il monitoraggio di inattivita' della sessione.
 * Mostra un avviso 5 minuti prima del logout automatico.
 */
function initIdleTimeout() {
    _resetIdleTimer();
    ['mousemove', 'keydown', 'click', 'scroll', 'touchstart'].forEach(evt => {
        document.addEventListener(evt, _resetIdleTimer, { passive: true });
    });
}

function _resetIdleTimer() {
    clearTimeout(_idleTimer);
    clearTimeout(_idleWarningTimer);
    clearInterval(_idleCountdownInterval);

    // Chiudi avviso se gia' aperto
    const existingWarning = document.getElementById('idle-warning-overlay');
    if (existingWarning) existingWarning.remove();

    // Timer per mostrare avviso
    _idleWarningTimer = setTimeout(_showIdleWarning, IDLE_TIMEOUT_MS - IDLE_WARNING_MS);

    // Timer per logout automatico
    _idleTimer = setTimeout(() => {
        if (typeof api !== 'undefined') api.logout();
    }, IDLE_TIMEOUT_MS);
}

function _showIdleWarning() {
    const existing = document.getElementById('idle-warning-overlay');
    if (existing) existing.remove();

    let remaining = Math.floor(IDLE_WARNING_MS / 1000);

    const overlay = document.createElement('div');
    overlay.id = 'idle-warning-overlay';
    overlay.style.cssText = [
        'position:fixed', 'top:0', 'left:0', 'right:0', 'bottom:0', 'z-index:9999',
        'background:rgba(0,0,0,0.55)', 'display:flex', 'align-items:center', 'justify-content:center'
    ].join(';');

    const _t = (key, fallback) => (typeof I18n !== 'undefined' ? I18n.t(key) : fallback);
    overlay.innerHTML = `
        <div style="background:var(--card-bg,#fff);border-radius:12px;padding:32px;max-width:400px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3);">
            <div style="width:56px;height:56px;background:#fff7ed;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">
                <svg width="28" height="28" viewBox="0 0 20 20" fill="#c2410c"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
            </div>
            <h3 style="font-size:1.1rem;font-weight:700;color:var(--gray-800,#1f2937);margin-bottom:8px;">${_t('session.expiring_title', 'Sessione in scadenza')}</h3>
            <p style="font-size:0.875rem;color:var(--gray-500,#6b7280);margin-bottom:20px;">
                ${_t('session.expiring_msg', 'Per motivi di sicurezza, verrai disconnesso tra')}<br>
                <strong id="idle-countdown" style="font-size:1.5rem;color:#c2410c;display:block;margin:8px 0;">5:00</strong>
                ${_t('session.idle_reason', "a causa di inattivita'.")}
            </p>
            <div style="display:flex;gap:12px;justify-content:center;">
                <button id="idle-stay-btn"
                    style="padding:10px 24px;background:var(--primary,#2563eb);color:#fff;border:none;border-radius:8px;font-weight:600;cursor:pointer;font-size:0.875rem;">
                    ${_t('session.stay', 'Rimani connesso')}
                </button>
                <button onclick="api.logout();"
                    style="padding:10px 24px;background:var(--gray-100,#f3f4f6);color:var(--gray-700,#374151);border:none;border-radius:8px;font-weight:600;cursor:pointer;font-size:0.875rem;">
                    ${_t('session.logout_now', 'Disconnetti')}
                </button>
            </div>
        </div>
    `;

    document.body.appendChild(overlay);

    document.getElementById('idle-stay-btn').addEventListener('click', () => {
        overlay.remove();
        _resetIdleTimer();
    });

    _idleCountdownInterval = setInterval(() => {
        remaining--;
        const min = Math.floor(remaining / 60);
        const sec = remaining % 60;
        const el = document.getElementById('idle-countdown');
        if (el) el.textContent = `${min}:${sec.toString().padStart(2, '0')}`;
        if (remaining <= 0) clearInterval(_idleCountdownInterval);
    }, 1000);
}


// ═══════════════════════════════════════════════════════════════════
// Utilities
// ═══════════════════════════════════════════════════════════════════

/**
 * Escape HTML per prevenire XSS.
 * @param {string} str
 * @returns {string}
 */
function escapeHtml(str) {
    if (!str) return '';
    const div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
}

/**
 * Debounce: ritarda l'esecuzione di fn fino a delay ms dopo l'ultima chiamata.
 * @param {Function} fn
 * @param {number} delay
 * @returns {Function}
 */
function debounce(fn, delay = 300) {
    let timer;
    return function (...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

/**
 * Restituisce la classe CSS per un punteggio di conformita'.
 * @param {number} score - Valore 0-100
 * @returns {string}
 */
function getScoreClass(score) {
    if (score >= 80) return 'score-excellent';
    if (score >= 60) return 'score-good';
    if (score >= 40) return 'score-medium';
    if (score >= 20) return 'score-low';
    return 'score-critical';
}

/**
 * Restituisce il colore esadecimale per un punteggio.
 * @param {number} score - Valore 0-100
 * @returns {string}
 */
function getScoreColor(score) {
    if (score >= 80) return '#34a853';
    if (score >= 60) return '#84cc16';
    if (score >= 40) return '#fbbc04';
    if (score >= 20) return '#f97316';
    return '#ea4335';
}

/**
 * Crea l'SVG per il gauge circolare del punteggio.
 * @param {number} score - Valore 0-100
 * @param {number} size - Dimensione in px (default 180)
 * @returns {string} HTML
 */
function renderScoreGauge(score, size = 180) {
    const radius = (size / 2) - 14;
    const circumference = 2 * Math.PI * radius;
    const offset = circumference - (score / 100) * circumference;
    const color = getScoreColor(score);
    const cls = getScoreClass(score);

    return `
        <div class="score-gauge ${cls}" style="width:${size}px;height:${size}px">
            <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
                <circle class="score-gauge-circle-bg" cx="${size/2}" cy="${size/2}" r="${radius}"/>
                <circle class="score-gauge-circle" cx="${size/2}" cy="${size/2}" r="${radius}"
                    stroke="${color}"
                    stroke-dasharray="${circumference}"
                    stroke-dashoffset="${offset}"
                    style="--score-color: ${color}"/>
            </svg>
            <div class="score-gauge-value">
                <div class="score-gauge-number">${Math.round(score)}</div>
                <div class="score-gauge-label">Compliance</div>
            </div>
        </div>
    `;
}


// ═══════════════════════════════════════════════════════════════════
// SVG Icons (inline, nessuna dipendenza esterna)
// ═══════════════════════════════════════════════════════════════════

function iconGrid() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zm0 8a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zm6-6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zm0 8a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/></svg>';
}

function iconClipboardCheck() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"/><path fill-rule="evenodd" d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm9.707 5.707a1 1 0 00-1.414-1.414L9 12.586l-1.293-1.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>';
}

function iconShieldExclamation() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 1.944A11.954 11.954 0 012.166 5C2.056 5.649 2 6.319 2 7c0 5.225 3.34 9.67 8 11.317C14.66 16.67 18 12.225 18 7c0-.682-.057-1.35-.166-2.001A11.954 11.954 0 0110 1.944zM11 14a1 1 0 11-2 0 1 1 0 012 0zm0-7a1 1 0 10-2 0v3a1 1 0 102 0V7z" clip-rule="evenodd"/></svg>';
}

function iconBell() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/></svg>';
}

function iconDocumentText() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/></svg>';
}

function iconLink() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd"/></svg>';
}

function iconAcademicCap() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10.394 2.08a1 1 0 00-.788 0l-7 3a1 1 0 000 1.84L5.25 8.051a.999.999 0 01.356-.257l4-1.714a1 1 0 11.788 1.838L7.667 9.088l1.94.831a1 1 0 00.787 0l7-3a1 1 0 000-1.838l-7-3zM3.31 9.397L5 10.12v4.102a8.969 8.969 0 00-1.05-.174 1 1 0 01-.89-.89 11.115 11.115 0 01.25-3.762zM9.3 16.573A9.026 9.026 0 007 14.935v-3.957l1.818.78a3 3 0 002.364 0l5.508-2.361a11.026 11.026 0 01.25 3.762 1 1 0 01-.89.89 8.968 8.968 0 00-5.35 2.524 1 1 0 01-1.4 0z"/></svg>';
}

function iconServer() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M2 5a2 2 0 012-2h12a2 2 0 012 2v2a2 2 0 01-2 2H4a2 2 0 01-2-2V5zm14 1a1 1 0 11-2 0 1 1 0 012 0zM2 13a2 2 0 012-2h12a2 2 0 012 2v2a2 2 0 01-2 2H4a2 2 0 01-2-2v-2zm14 1a1 1 0 11-2 0 1 1 0 012 0z" clip-rule="evenodd"/></svg>';
}

function iconChartBar() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zm6-4a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zm6-3a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/></svg>';
}

function iconCubeTransparent() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10 3.5a1 1 0 00-.5.134l-5 2.857A1 1 0 004 7.382v5.236a1 1 0 00.5.866l5 2.857a1 1 0 001 0l5-2.857a1 1 0 00.5-.866V7.382a1 1 0 00-.5-.891l-5-2.857A1 1 0 0010 3.5zM6 8.382l4 2.286 4-2.286v3.236l-4 2.286-4-2.286V8.382z"/></svg>';
}

function iconQuestionMarkCircle() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/></svg>';
}

function iconCog() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/></svg>';
}

/**
 * Imposta lo stato di caricamento su un bottone.
 * @param {HTMLElement|string} btn - Elemento o ID
 * @param {boolean} loading - true per attivare, false per disattivare
 * @param {string} [originalText] - Testo originale da ripristinare
 */
function setButtonLoading(btn, loading, originalText) {
    if (typeof btn === 'string') btn = document.getElementById(btn);
    if (!btn) return;
    if (loading) {
        btn._originalText = btn.textContent;
        btn.classList.add('loading');
        btn.disabled = true;
    } else {
        btn.classList.remove('loading');
        btn.disabled = false;
        if (originalText) btn.textContent = originalText;
        else if (btn._originalText) btn.textContent = btn._originalText;
    }
}

function iconPlus() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd"/></svg>';
}

function iconSparkles() {
    return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zm7-10a1 1 0 01.967.744L14.146 7.2 17.5 8.512a1 1 0 010 1.836l-3.354 1.311-1.18 4.456a1 1 0 01-1.932 0L9.854 11.66 6.5 10.348a1 1 0 010-1.836l3.354-1.311 1.18-4.456A1 1 0 0112 2z"/></svg>';
}


// ═══════════════════════════════════════════════════════════════════
// Language Switcher
// ═══════════════════════════════════════════════════════════════════

function _getSavedLang() {
    return localStorage.getItem('nis2_lang') || 'it';
}

function switchLang(lang) {
    if (typeof I18n !== 'undefined') {
        I18n.setLang(lang);
    }
    localStorage.setItem('nis2_lang', lang);
    // Aggiorna bottoni
    document.querySelectorAll('.lang-btn').forEach(btn => {
        btn.classList.toggle('active', btn.textContent.trim().toLowerCase() === lang);
    });
}

// Inject language toggle CSS
(function _injectLangCSS() {
    if (document.getElementById('lang-toggle-css')) return;
    const style = document.createElement('style');
    style.id = 'lang-toggle-css';
    style.textContent = `
        .sidebar-lang-toggle {
            display: flex;
            justify-content: center;
            gap: 4px;
            padding: 8px 16px 4px;
        }
        .lang-btn {
            padding: 4px 14px;
            font-size: 0.75rem;
            font-weight: 700;
            border: 1px solid var(--gray-300);
            background: var(--gray-50);
            color: var(--gray-500);
            border-radius: 4px;
            cursor: pointer;
            transition: all var(--transition-fast);
            font-family: inherit;
        }
        .lang-btn:hover {
            background: var(--gray-100);
            color: var(--gray-700);
        }
        .lang-btn.active {
            background: var(--primary);
            color: #fff;
            border-color: var(--primary);
        }
    `;
    document.head.appendChild(style);
})();

// ── Auto-inject feedback.js su tutte le pagine autenticate ──────────────────
(function () {
    const excluded = ['login.html', 'register.html', 'onboarding.html', 'index.html', ''];
    const page = window.location.pathname.split('/').pop();
    if (excluded.includes(page)) return;
    if (document.querySelector('script[src*="feedback.js"]')) return;
    const s = document.createElement('script');
    s.src = 'js/feedback.js';
    document.body.appendChild(s);
})();

/* ════════════════════════════════════════════════════════════════════════
   AgileHub / Nexus integration  (NIS2)
   ─────────────────────────────────────────────────────────────────────────
   - bug-reporter.js viene iniettato dinamicamente DOPO il login
     (richiede i data-user-* del profilo corrente). Idempotente.
   - FAB AI viola "ARIA" creato via JS (niente modifiche alle 18 pagine HTML).
   - Si collega all'AI nativa NIS2 per il grounding KB Multi-Livello.
   ════════════════════════════════════════════════════════════════════════ */

(function () {
    'use strict';

    // Salta nelle pagine pubbliche (login/register/landing/marketing).
    var publicPages = ['/login.html', '/register.html', '/index.html', '/presentation.html', '/'];
    var path = location.pathname.replace(/^.*\//, '/');
    if (publicPages.indexOf(path) !== -1) return;

    var token = localStorage.getItem('nis2_access_token');
    if (!token) return; // utente non loggato → skip

    // Mini JWT decoder (base64url → JSON payload)
    function decodeJwt(t) {
        try {
            var b64 = t.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
            return JSON.parse(decodeURIComponent(escape(atob(b64))));
        } catch (e) { return {}; }
    }

    var claims = decodeJwt(token);
    var userEmail = claims.email || '';
    var userName  = claims.name || claims.full_name || claims.email || '';
    var userRole  = localStorage.getItem('nis2_user_role') || claims.role || '';

    // ── 1. Inietta il bug-reporter Nexus ────────────────────────────────────
    if (!window.__nexusWidgetLoaded) {
        window.__nexusWidgetLoaded = true;
        var s = document.createElement('script');
        s.src = 'js/bug-reporter.js?v=20260411';
        s.async = true;
        s.dataset.product   = 'NIS2';
        s.dataset.tenantId  = '7';
        s.dataset.apiUrl    = 'https://agilehub.agile.software';
        s.dataset.userName  = userName;
        s.dataset.userEmail = userEmail;
        s.dataset.userRole  = userRole;
        s.dataset.lang      = 'it';
        document.body.appendChild(s);
    }

    // ── 2. AI Chat FAB viola "ARIA" ─────────────────────────────────────────
    function ensureFab() {
        if (document.getElementById('ai-chat-fab')) return;

        var fab = document.createElement('button');
        fab.id = 'ai-chat-fab';
        fab.setAttribute('aria-label', 'Chiedi ad ARIA');
        fab.style.cssText = 'position:fixed;bottom:24px;right:24px;width:56px;height:56px;'
            + 'border-radius:50%;border:none;cursor:pointer;z-index:9998;'
            + 'background:linear-gradient(135deg,#7C3AED,#3B82F6);color:#fff;'
            + 'box-shadow:0 6px 20px rgba(124,58,237,.4);font-size:22px;'
            + 'display:flex;align-items:center;justify-content:center;';
        fab.innerHTML = '<i class="fa-solid fa-wand-magic-sparkles"></i>';
        document.body.appendChild(fab);

        var panel = document.createElement('div');
        panel.id = 'ai-chat-panel';
        panel.style.cssText = 'position:fixed;bottom:90px;right:24px;width:360px;'
            + 'max-height:520px;background:#fff;border-radius:16px;'
            + 'box-shadow:0 12px 32px rgba(0,0,0,.2);display:none;z-index:9999;'
            + 'overflow:hidden;flex-direction:column;'
            + "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;";
        document.body.appendChild(panel);

        var STORAGE_KEY = 'nis2_ai_chat';
        var history = [];
        try { history = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '[]'); } catch (e) {}

        function escHtml(s) {
            return String(s).replace(/[&<>"']/g, function (c) {
                return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c];
            });
        }

        function render() {
            var list = history.map(function (m) {
                var isUser = m.role === 'user';
                return '<div style="margin-bottom:10px;display:flex;justify-content:'
                    + (isUser ? 'flex-end' : 'flex-start') + '">'
                    + '<div style="max-width:78%;padding:8px 12px;border-radius:14px;'
                    + 'font-size:13px;line-height:1.45;background:'
                    + (isUser ? 'linear-gradient(135deg,#7C3AED,#3B82F6)' : '#f3f4f6')
                    + ';color:' + (isUser ? '#fff' : '#111827')
                    + ';white-space:pre-wrap">' + escHtml(m.content) + '</div></div>';
            }).join('');
            panel.innerHTML =
                '<div style="padding:14px 16px;background:linear-gradient(135deg,#7C3AED,#3B82F6);'
                + 'color:#fff;display:flex;justify-content:space-between;align-items:center">'
                +   '<div style="display:flex;align-items:center;gap:8px;font-weight:600">'
                +     '<i class="fa-solid fa-wand-magic-sparkles"></i> ARIA — Assistente NIS2'
                +   '</div>'
                +   '<button id="ai-chat-close" aria-label="Chiudi" style="background:none;'
                +   'border:none;color:#fff;font-size:18px;cursor:pointer">&times;</button>'
                + '</div>'
                + '<div id="ai-chat-msgs" style="flex:1;overflow-y:auto;padding:14px 16px;'
                + 'background:#fafafa">'
                + (list || '<div style="color:#9ca3af;font-size:13px;text-align:center;'
                +          'margin-top:24px">Ciao! Chiedimi qualcosa su NIS2: misure di '
                +          'sicurezza, audit, fornitori, incident response…</div>')
                + '</div>'
                + '<form id="ai-chat-form" style="display:flex;gap:6px;padding:10px;'
                +     'border-top:1px solid #e5e7eb;background:#fff">'
                +   '<input id="ai-chat-input" type="text" placeholder="Scrivi…" '
                +     'autocomplete="off" style="flex:1;padding:10px 12px;border:1px solid '
                +     '#d1d5db;border-radius:10px;font-size:14px;font-family:inherit">'
                +   '<button type="submit" style="padding:10px 14px;border:none;'
                +     'border-radius:10px;background:linear-gradient(135deg,#7C3AED,#3B82F6);'
                +     'color:#fff;font-weight:600;cursor:pointer">'
                +     '<i class="fa-solid fa-paper-plane"></i>'
                +   '</button>'
                + '</form>';
            document.getElementById('ai-chat-close').onclick = function () {
                panel.style.display = 'none';
            };
            document.getElementById('ai-chat-form').onsubmit = onSubmit;
            var msgs = document.getElementById('ai-chat-msgs');
            msgs.scrollTop = msgs.scrollHeight;
        }

        async function onSubmit(ev) {
            ev.preventDefault();
            var input = document.getElementById('ai-chat-input');
            var text  = (input.value || '').trim();
            if (!text) return;
            history.push({ role: 'user', content: text });
            sessionStorage.setItem(STORAGE_KEY, JSON.stringify(history));
            render();
            try {
                // AI nativa NIS2 (RAG con KB Multi-Livello già attivo)
                var t = localStorage.getItem('nis2_access_token') || '';
                var orgId = localStorage.getItem('nis2_org_id') || '';
                var r = await fetch('/api/ai/ask', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': t ? ('Bearer ' + t) : '',
                        'X-Organization-Id': orgId
                    },
                    body: JSON.stringify({ question: text, history: history })
                });
                var resp = await r.json();
                var answer = (resp && resp.success && resp.data
                              && (resp.data.answer || resp.data.message || resp.data.text))
                          || (resp && (resp.answer || resp.message || resp.text))
                          || 'ARIA non ha risposto. Riprova tra poco.';
                history.push({ role: 'assistant', content: answer });
            } catch (e) {
                history.push({
                    role: 'assistant',
                    content: '⚠️ ARIA non risponde in questo momento. Riprova tra poco.'
                });
            }
            sessionStorage.setItem(STORAGE_KEY, JSON.stringify(history));
            render();
        }

        fab.onclick = function () {
            var visible = panel.style.display === 'flex';
            panel.style.display = visible ? 'none' : 'flex';
            if (!visible) render();
        };
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', ensureFab);
    } else {
        ensureFab();
    }
})();
