<!-- Node Health Module — Trinity Console -->
<!-- Chronicler #88 | April 14, 2026 -->

<style>
  @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');

  #node-health { font-family: 'JetBrains Mono', 'SF Mono', monospace; color: #e0e0e0; }
  #node-health * { box-sizing: border-box; }

  .nh-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin-top: 16px;
  }
  @media (max-width: 900px) {
    .nh-grid { grid-template-columns: 1fr; }
  }

  .nh-node-card {
    background: #2d2d2d;
    border: 1px solid #404040;
    border-radius: 10px;
    overflow: hidden;
  }
  .nh-node-header {
    padding: 14px 18px;
    display: flex;
    align-items: center;
    gap: 10px;
    border-bottom: 1px solid #404040;
  }
  .nh-node-dot {
    width: 10px; height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .nh-node-title { font-size: 15px; font-weight: 700; }
  .nh-node-role  { font-size: 10px; color: #888; margin-left: auto; }

  .nh-body { padding: 16px 18px; }

  .nh-section-title {
    font-size: 9px;
    font-weight: 700;
    color: #666;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin: 14px 0 8px;
  }
  .nh-section-title:first-child { margin-top: 0; }

  /* Stat row */
  .nh-stat-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 5px 0;
    border-bottom: 1px solid #333;
    font-size: 12px;
  }
  .nh-stat-row:last-child { border-bottom: none; }
  .nh-stat-label { color: #888; }
  .nh-stat-value { font-weight: 600; color: #e0e0e0; text-align: right; }

  /* Temp row — shows both C and F */
  .nh-temp-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 5px 0;
    border-bottom: 1px solid #333;
    font-size: 12px;
  }
  .nh-temp-row:last-child { border-bottom: none; }
  .nh-temp-label { color: #888; }
  .nh-temp-values { display: flex; gap: 10px; align-items: center; }
  .nh-temp-c { font-weight: 700; }
  .nh-temp-f { color: #777; font-size: 11px; }

  /* Color thresholds */
  .temp-ok     { color: #4ade80; }
  .temp-warm   { color: #facc15; }
  .temp-hot    { color: #f97316; }
  .temp-crit   { color: #ef4444; }

  /* Progress bars */
  .nh-bar-wrap {
    background: #1a1a1a;
    border-radius: 4px;
    height: 6px;
    margin-top: 4px;
    overflow: hidden;
  }
  .nh-bar {
    height: 100%;
    border-radius: 4px;
    transition: width 0.4s ease;
  }
  .bar-ok   { background: #4ade80; }
  .bar-warn { background: #facc15; }
  .bar-crit { background: #ef4444; }

  .nh-bar-label {
    display: flex;
    justify-content: space-between;
    font-size: 11px;
    color: #888;
    margin-top: 3px;
  }

  /* Load averages */
  .nh-load-row {
    display: flex;
    gap: 10px;
  }
  .nh-load-pill {
    flex: 1;
    background: #1a1a1a;
    border-radius: 6px;
    padding: 8px;
    text-align: center;
  }
  .nh-load-val { font-size: 16px; font-weight: 700; color: #e0e0e0; }
  .nh-load-lbl { font-size: 9px; color: #666; margin-top: 2px; }

  /* Uptime / CPU row */
  .nh-info-row {
    display: flex;
    gap: 10px;
    margin-bottom: 4px;
  }
  .nh-info-pill {
    flex: 1;
    background: #1a1a1a;
    border-radius: 6px;
    padding: 8px 10px;
  }
  .nh-info-pill .val { font-size: 13px; font-weight: 700; }
  .nh-info-pill .lbl { font-size: 9px; color: #666; margin-top: 2px; }

  /* Offline state */
  .nh-offline {
    text-align: center;
    padding: 40px;
    color: #ef4444;
    font-size: 13px;
  }

  /* Header row */
  .nh-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 4px;
  }
  .nh-last-updated {
    font-size: 10px;
    color: #555;
  }
  .nh-refresh-btn {
    background: #333;
    border: 1px solid #555;
    border-radius: 6px;
    color: #aaa;
    font-family: inherit;
    font-size: 11px;
    padding: 5px 12px;
    cursor: pointer;
    transition: background 0.2s;
  }
  .nh-refresh-btn:hover { background: #444; color: #fff; }
  .nh-refresh-btn:disabled { opacity: 0.5; cursor: not-allowed; }
  .nh-spinner { display: none; }
  .nh-refreshing .nh-spinner { display: inline; }
  .nh-refreshing .nh-btn-text { display: none; }
</style>

<div id="node-health">

  <div class="nh-header-row">
    <div>
      <h2 style="margin:0; font-size:18px; font-weight:700;">Node Health</h2>
      <div class="nh-last-updated" id="nh-last-updated">
        <% if (nodes) { %>Last updated: <%= new Date(nodes.fetchedAt).toLocaleTimeString() %><% } %>
      </div>
    </div>
    <button class="nh-refresh-btn" id="nh-refresh-btn" onclick="refreshNodeHealth()">
      <span class="nh-btn-text">↻ Refresh</span>
      <span class="nh-spinner">Refreshing…</span>
    </button>
  </div>

  <div class="nh-grid" id="nh-grid">
    <% if (!nodes) { %>
      <div style="grid-column:1/-1; color:#ef4444; padding:20px;">Failed to load node data. <%= error || '' %></div>
    <% } else { %>
      <% ['nc1-charlotte', 'tx1-dallas'].forEach(function(id) { %>
        <% const n = nodes[id]; %>
        <% if (!n.online) { %>
          <div class="nh-node-card">
            <div class="nh-node-header">
              <div class="nh-node-dot" style="background:#ef4444;"></div>
              <span class="nh-node-title"><%= n.label %></span>
              <span class="nh-node-role"><%= n.role %></span>
            </div>
            <div class="nh-offline">⚠ Node unreachable</div>
          </div>
        <% } else { %>
          <div class="nh-node-card">
            <div class="nh-node-header">
              <div class="nh-node-dot" style="background:<%= n.color %>;"></div>
              <span class="nh-node-title"><%= n.label %></span>
              <span class="nh-node-role"><%= n.role %></span>
            </div>
            <div class="nh-body">

              <div class="nh-info-row">
                <div class="nh-info-pill">
                  <div class="val"><%= n.uptime %></div>
                  <div class="lbl">UPTIME</div>
                </div>
                <div class="nh-info-pill">
                  <div class="val"><%= n.cpuPct %>%</div>
                  <div class="lbl">CPU USAGE</div>
                </div>
              </div>

              <div class="nh-section-title">Load Average</div>
              <div class="nh-load-row">
                <div class="nh-load-pill">
                  <div class="nh-load-val"><%= n.load[0].toFixed(2) %></div>
                  <div class="nh-load-lbl">1 MIN</div>
                </div>
                <div class="nh-load-pill">
                  <div class="nh-load-val"><%= n.load[1].toFixed(2) %></div>
                  <div class="nh-load-lbl">5 MIN</div>
                </div>
                <div class="nh-load-pill">
                  <div class="nh-load-val"><%= n.load[2].toFixed(2) %></div>
                  <div class="nh-load-lbl">15 MIN</div>
                </div>
              </div>

              <div class="nh-section-title">Memory</div>
              <div class="nh-bar-wrap">
                <div class="nh-bar <%= n.ram.pct >= 85 ? 'bar-crit' : n.ram.pct >= 70 ? 'bar-warn' : 'bar-ok' %>" style="width:<%= n.ram.pct %>%"></div>
              </div>
              <div class="nh-bar-label">
                <span><%= n.ram.used %> used</span>
                <span><%= n.ram.pct %>% of <%= n.ram.total %></span>
              </div>

              <div class="nh-section-title">Disk</div>
              <div class="nh-bar-wrap">
                <div class="nh-bar <%= n.disk.pct >= 85 ? 'bar-crit' : n.disk.pct >= 70 ? 'bar-warn' : 'bar-ok' %>" style="width:<%= n.disk.pct %>%"></div>
              </div>
              <div class="nh-bar-label">
                <span><%= n.disk.used %> used</span>
                <span><%= n.disk.pct %>% of <%= n.disk.total %></span>
              </div>

              <div class="nh-section-title">Temperatures</div>
              <% if (n.temps && Object.keys(n.temps).length > 0) { %>
                <% Object.entries(n.temps).forEach(function([key, t]) { %>
                  <% const cls = t.c >= 85 ? 'temp-crit' : t.c >= 70 ? 'temp-hot' : t.c >= 55 ? 'temp-warm' : 'temp-ok'; %>
                  <div class="nh-temp-row">
                    <span class="nh-temp-label"><%= key %></span>
                    <div class="nh-temp-values">
                      <span class="nh-temp-c <%= cls %>"><%= t.c %>°C</span>
                      <span class="nh-temp-f"><%= t.f %>°F</span>
                    </div>
                  </div>
                <% }); %>
              <% } else { %>
                <div style="color:#666;font-size:11px;">No sensor data</div>
              <% } %>

            </div>
          </div>
        <% } %>
      <% }); %>
    <% } %>
  </div>

</div>

<script>
  // Auto-refresh every 30 seconds
  let refreshTimer = setInterval(refreshNodeHealth, 30000);

  async function refreshNodeHealth() {
    const btn = document.getElementById('nh-refresh-btn');
    btn.disabled = true;
    btn.classList.add('nh-refreshing');

    try {
      const res = await fetch('/admin/node-health/data');
      const json = await res.json();
      if (json.success) {
        renderNodes(json.nodes);
        document.getElementById('nh-last-updated').textContent =
          'Last updated: ' + new Date(json.nodes.fetchedAt).toLocaleTimeString();
      }
    } catch (err) {
      console.error('Node health refresh failed:', err);
    }

    btn.disabled = false;
    btn.classList.remove('nh-refreshing');
  }

  function tempClass(c) {
    if (c >= 85) return 'temp-crit';
    if (c >= 70) return 'temp-hot';
    if (c >= 55) return 'temp-warm';
    return 'temp-ok';
  }

  function barClass(pct) {
    if (pct >= 85) return 'bar-crit';
    if (pct >= 70) return 'bar-warn';
    return 'bar-ok';
  }

  function renderNodes(nodes) {
    const grid = document.getElementById('nh-grid');
    const ids = ['nc1-charlotte', 'tx1-dallas'];
    let html = '';
    ids.forEach(id => {
      const n = nodes[id];
      html += renderCard(id, n);
    });
    grid.innerHTML = html;
  }

  function renderCard(id, n) {
    if (!n.online) {
      return `<div class="nh-node-card">
        <div class="nh-node-header">
          <div class="nh-node-dot" style="background:#ef4444;"></div>
          <span class="nh-node-title">${n.label}</span>
          <span class="nh-node-role">${n.role}</span>
        </div>
        <div class="nh-offline">⚠ Node unreachable</div>
      </div>`;
    }

    // Temps
    let tempRows = '';
    if (n.temps) {
      for (const [key, t] of Object.entries(n.temps)) {
        const cls = tempClass(t.c);
        tempRows += `<div class="nh-temp-row">
          <span class="nh-temp-label">${key}</span>
          <div class="nh-temp-values">
            <span class="nh-temp-c ${cls}">${t.c}°C</span>
            <span class="nh-temp-f">${t.f}°F</span>
          </div>
        </div>`;
      }
    }

    // RAM bar
    const ramBar = barClass(n.ram.pct);
    // Disk bar
    const diskBar = barClass(n.disk.pct);

    return `<div class="nh-node-card">
      <div class="nh-node-header">
        <div class="nh-node-dot" style="background:${n.color};"></div>
        <span class="nh-node-title">${n.label}</span>
        <span class="nh-node-role">${n.role}</span>
      </div>
      <div class="nh-body">

        <div class="nh-info-row">
          <div class="nh-info-pill">
            <div class="val">${n.uptime}</div>
            <div class="lbl">UPTIME</div>
          </div>
          <div class="nh-info-pill">
            <div class="val">${n.cpuPct}%</div>
            <div class="lbl">CPU USAGE</div>
          </div>
        </div>

        <div class="nh-section-title">Load Average</div>
        <div class="nh-load-row">
          <div class="nh-load-pill">
            <div class="nh-load-val">${n.load[0].toFixed(2)}</div>
            <div class="nh-load-lbl">1 MIN</div>
          </div>
          <div class="nh-load-pill">
            <div class="nh-load-val">${n.load[1].toFixed(2)}</div>
            <div class="nh-load-lbl">5 MIN</div>
          </div>
          <div class="nh-load-pill">
            <div class="nh-load-val">${n.load[2].toFixed(2)}</div>
            <div class="nh-load-lbl">15 MIN</div>
          </div>
        </div>

        <div class="nh-section-title">Memory</div>
        <div class="nh-bar-wrap">
          <div class="nh-bar ${ramBar}" style="width:${n.ram.pct}%"></div>
        </div>
        <div class="nh-bar-label">
          <span>${n.ram.used} used</span>
          <span>${n.ram.pct}% of ${n.ram.total}</span>
        </div>

        <div class="nh-section-title">Disk</div>
        <div class="nh-bar-wrap">
          <div class="nh-bar ${diskBar}" style="width:${n.disk.pct}%"></div>
        </div>
        <div class="nh-bar-label">
          <span>${n.disk.used} used</span>
          <span>${n.disk.pct}% of ${n.disk.total}</span>
        </div>

        <div class="nh-section-title">Temperatures</div>
        ${tempRows || '<div style="color:#666;font-size:11px;">No sensor data</div>'}

      </div>
    </div>`;
  }
</script>
