const Grid = (() => {
    const _agents = new Map();

    function statusClass(agent) {
        if (agent.status === 'offline') return 't-off';
        const m = _agents.get(agent.id)?.metrics;
        if (!m) return '';
        const cfg = App.serverConfig;
        const errThreshold = cfg?.err_cpu ?? 85;
        const warnThreshold = cfg?.warn_cpu ?? 70;
        if ((m.cpu_percent ?? 0) >= errThreshold) return 't-err';
        if ((m.cpu_percent ?? 0) >= warnThreshold || (m.hdd_used && m.hdd_total && (m.hdd_used / m.hdd_total * 100) >= (cfg?.warn_disk ?? 75))) return 't-warn';
        return '';
    }

    function ledClass(status) {
        return { online: 's-ok', warn: 's-warn', err: 's-err', offline: 's-off' }[status] ?? 's-off';
    }

    function fmt(bytes) {
        if (!bytes) return '—';
        if (bytes < 1024) return bytes + 'o';
        if (bytes < 1024 ** 2) return (bytes / 1024).toFixed(1) + 'Ko';
        if (bytes < 1024 ** 3) return (bytes / 1024 ** 2).toFixed(1) + 'Mo';
        return (bytes / 1024 ** 3).toFixed(1) + 'Go';
    }

    function fmtPct(val) {
        return val != null ? val.toFixed(0) + '%' : '—';
    }

    function gFill(pct) {
        const cfg = App.serverConfig;
        if (pct >= (cfg?.err_cpu ?? 85)) return 'e';
        if (pct >= (cfg?.warn_cpu ?? 70)) return 'w';
        return '';
    }

    function renderGaugeRow(faIcon, tip, label, pct, fillClass, valStr, extra) {
        const standard = (App.serverConfig?.gauge_type ?? 'compact') === 'standard';
        if (standard) {
            return `<div class="gs-row">
              <div class="gs-header">
                <span class="gs-ico" data-tip="${tip}"><i class="fa-solid fa-${faIcon}"></i></span>
                <span class="gs-lbl">${label}</span>
                <span class="gs-val">${valStr}</span>${extra || ''}
              </div>
              <div class="gs-bar">
                <div class="gs-fill ${fillClass}" style="width:${(pct ?? 0).toFixed(1)}%"></div>
                <div class="gs-gloss"></div>
              </div>
            </div>`;
        }
        return `<div class="g-row">
          <div class="g-ico" data-tip="${tip}"><i class="fa-solid fa-${faIcon}"></i></div>
          <div class="g-bar"><div class="g-fill ${fillClass}" style="width:${(pct ?? 0).toFixed(0)}%"></div></div>
          <span class="g-val">${valStr}</span>${extra || ''}
        </div>`;
    }

    function renderTile(agent, metrics) {
        const id = agent.id;
        const sc = statusClass(agent);
        const offline = agent.status === 'offline';

        const cpu = metrics?.cpu_percent ?? null;
        const memPct = (metrics?.memory_used && metrics?.memory_total)
            ? metrics.memory_used / metrics.memory_total * 100 : null;
        const diskPct = (metrics?.hdd_used && metrics?.hdd_total)
            ? metrics.hdd_used / metrics.hdd_total * 100 : null;

        const uptimeSec = metrics?.uptime;
        let uptimeStr = '';
        if (uptimeSec) {
            const d = Math.floor(uptimeSec / 86400);
            const h = Math.floor((uptimeSec % 86400) / 3600);
            uptimeStr = d > 0 ? `${d}j ${h}h` : `${h}h`;
        }

        const smartIco = !offline && metrics?.smart != null
            ? (metrics.smart.passed
                ? `<i class="fa-solid fa-shield-check" style="color:var(--ok);font-size:10px;flex-shrink:0" data-tip="SMART OK"></i>`
                : `<i class="fa-solid fa-triangle-exclamation" style="color:var(--err);font-size:10px;flex-shrink:0" data-tip="SMART FAILED"></i>`)
            : '';

        const iconContent = `<img src="${API.iconUrl(id)}" alt=""
            style="width:100%;height:100%;object-fit:cover;border-radius:7px"
            onerror="this.style.display='none';this.nextSibling.style.display='flex'">
            <span style="display:none;align-items:center;justify-content:center;width:100%;height:100%;color:var(--accent)">
              <i class="fa-solid fa-server"></i></span>`;

        return `<div class="tile ${sc}" id="tile-${id}" onclick="Popups.showDetail('${esc(id)}')">
          <div class="tile-head">
            <div class="t-icon">${iconContent}</div>
            <div class="t-names">
              <div class="t-host">${esc(agent.hostname)}</div>
              <div class="t-ip">${esc(agent.ip) || '—'}</div>
            </div>
            <div class="t-led ${ledClass(agent.status)}"></div>
          </div>
          <div class="tile-gauges">
            ${renderGaugeRow('microchip', 'CPU', 'CPU',
                offline ? 0 : (cpu ?? 0),
                offline ? '' : gFill(cpu ?? 0),
                offline ? '—' : fmtPct(cpu))}
            ${renderGaugeRow('memory', 'RAM', 'MÉMOIRE',
                offline ? 0 : (memPct ?? 0),
                offline ? '' : gFill(memPct ?? 0),
                offline ? '—' : (metrics?.memory_used && metrics?.memory_total ? fmt(metrics.memory_used) + '/' + fmt(metrics.memory_total) : '—'))}
            ${renderGaugeRow('hard-drive', 'Disque', 'DISQUE',
                offline ? 0 : (diskPct ?? 0),
                offline ? '' : (diskPct >= (App.serverConfig?.warn_disk ?? 75) ? 'w' : ''),
                offline ? '—' : (metrics?.hdd_used && metrics?.hdd_total ? fmt(metrics.hdd_used) + '/' + fmt(metrics.hdd_total) : '—'),
                smartIco)}
          </div>
          <div class="tile-foot">
            <span class="tile-foot-info">
              ${offline
                ? '<i class="fa-solid fa-circle-xmark" style="color:var(--err)"></i><span style="color:var(--err)">Hors ligne</span>'
                : `<i class="fa-solid fa-clock"></i><span>${uptimeStr || '—'}</span>`}
            </span>
            <button class="btn-del-agent" title="Supprimer cet agent"
              onclick="event.stopPropagation();Popups.confirmDeleteAgent('${esc(id)}','${esc(agent.hostname)}')">
              <i class="fa-solid fa-trash"></i>
            </button>
          </div>
        </div>`;
    }

    function update(agentId, metrics) {
        let entry = _agents.get(agentId);
        if (!entry) {
            // Nouvel agent découvert via WebSocket — on crée la tuile à la volée
            const agent = {
                id: agentId,
                hostname: metrics.hostname || agentId,
                ip: metrics.ip || '',
                status: metrics.status || 'online',
            };
            _agents.set(agentId, { agent, metrics });
            const grid = document.getElementById('agents-grid');
            if (grid) grid.insertAdjacentHTML('beforeend', renderTile(agent, metrics));
            updateStats();
            return;
        }
        // Mettre à jour ip/status depuis les métriques fraîches
        if (metrics.ip) entry.agent.ip = metrics.ip;
        if (metrics.status) entry.agent.status = metrics.status;
        // Conserver les valeurs lentes (disque, smart) quand le paquet ne les contient pas
        if (entry.metrics) {
            for (const k of Object.keys(entry.metrics)) {
                if (metrics[k] == null && entry.metrics[k] != null) metrics[k] = entry.metrics[k];
            }
        }
        entry.metrics = metrics;
        const el = document.getElementById('tile-' + agentId);
        if (el) {
            el.outerHTML = renderTile(entry.agent, metrics);
        }
        updateStats();
    }

    function refresh(agents) {
        agents.forEach(a => {
            if (!_agents.has(a.id)) {
                _agents.set(a.id, { agent: a, metrics: a.last_metrics || null });
            } else {
                _agents.get(a.id).agent = a;
            }
        });
        const grid = document.getElementById('agents-grid');
        if (!grid) return;
        grid.innerHTML = agents.map(a => renderTile(a, _agents.get(a.id)?.metrics)).join('');
        updateStats();
    }

    function updateStats() {
        let total = 0, ok = 0, warn = 0, err = 0;
        _agents.forEach(({ agent }) => {
            total++;
            const sc = statusClass(agent);
            if (agent.status === 'offline') {}
            else if (sc === 't-err') err++;
            else if (sc === 't-warn') warn++;
            else ok++;
        });
        document.getElementById('stat-total').textContent = total;
        document.getElementById('stat-ok').textContent = ok;
        document.getElementById('stat-warn').textContent = warn;
        document.getElementById('stat-err').textContent = err;
    }

    function rerenderAll() {
        const grid = document.getElementById('agents-grid');
        if (!grid) return;
        grid.innerHTML = [..._agents.values()].map(({ agent, metrics }) => renderTile(agent, metrics)).join('');
    }

    function removeAgent(id) {
        _agents.delete(id);
        const el = document.getElementById('tile-' + id);
        if (el) el.remove();
        updateStats();
    }

    function getAgent(id) { return _agents.get(id); }

    function updateStatus(agentId, status) {
        const entry = _agents.get(agentId);
        if (!entry) return;
        entry.agent.status = status;
        const el = document.getElementById('tile-' + agentId);
        if (el) el.outerHTML = renderTile(entry.agent, entry.metrics);
        updateStats();
    }

    return { refresh, update, updateStatus, removeAgent, rerenderAll, getAgent, fmt, fmtPct };
})();
