---
// Featured LIVE server card. Progressive enhancement:
//   SSG  → renders a static skeleton (server icon, MOTD, slot cap) identical in
//          spirit to the old ServerListPanel, so it looks right with JS off or
//          if the status API is unreachable.
//   Client → fetches mcstatus.io (CORS:*, no key, 60s cache) for the public
//          address and fills in: live online/offline pill, players online/max +
//          fill bar, the real server favicon, color MOTD, and a row of player
//          HEADS rendered by OUR NMSR from Drasl skins (not Mojang/Crafatar).
//
// Live labels are passed as data-* so copy stays translatable from the page
// (defaults are English). Heads use /headiso. Promo/placeholder players with
// the all-zero UUID are filtered out.
import Creeper from "./Creeper.astro";
import { site } from "../data/site";

interface Props {
  // SSG fallback copy (same props the old ServerListPanel took).
  modded: string;
  motd: string;
  upTo: string;
  // Live labels (optional, English defaults). Pass t.* from the page to i18n.
  onlineLabel?: string;
  offlineLabel?: string;
  emptyLabel?: string;
}
const {
  modded,
  motd,
  upTo,
  onlineLabel = "Online",
  offlineLabel = "Offline",
  emptyLabel = "Nobody online — be the first.",
} = Astro.props;

const { status, avatarUrl } = site;
const HEAD_MODE = "headiso"; // NMSR render mode for the head row
---
<div
  class="servercard"
  data-api={status.statusApi}
  data-avatar={avatarUrl}
  data-mode={HEAD_MODE}
  data-slots={status.slots}
  data-online={onlineLabel}
  data-offline={offlineLabel}
  data-empty={emptyLabel}
>
  <div class="sc-top">
    <div class="sc-icon">
      <!-- Replaced by the real server favicon on success; Creeper otherwise. -->
      <img class="sc-favicon" alt="" hidden />
      <span class="sc-creeper"><Creeper /></span>
    </div>

    <div class="sc-meta">
      <div class="sc-row1">
        <span class="sc-title">{site.name}</span>
        <span class="sc-ver">Java {site.mcVersion}</span>
      </div>
      <p class="sc-motd"><span class="a">⛏ {modded}</span> · <span class="sc-motd-text">{motd}</span></p>
    </div>

    <div class="sc-state">
      <span class="sc-pill" data-up="">
        <span class="sc-dot"></span>
        <span class="sc-pill-txt">·</span>
      </span>
      <div class="sc-count">
        <span class="sc-on">{upTo}</span> <b class="sc-max">{status.slots}</b>
      </div>
    </div>
  </div>

  <!-- Player fill bar (online/max). Hidden until a live count arrives. -->
  <div class="sc-bar" hidden><span class="sc-bar-fill"></span></div>

  <!-- Head row + empty state. Populated client-side. -->
  <div class="sc-players" hidden>
    <div class="sc-heads" aria-label="Players online"></div>
    <p class="sc-empty" hidden></p>
  </div>
</div>

<style>
  .servercard {
    background: var(--slot);
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    box-shadow:
      inset 2px 2px 0 var(--bevel-hi),
      inset -2px -2px 0 var(--bevel-lo),
      0 8px 24px oklch(0 0 0 / 0.4);
  }
  .sc-top { display: flex; gap: 18px; align-items: center; }

  .sc-icon {
    width: 72px; height: 72px; flex-shrink: 0;
    display: grid; place-items: center; position: relative;
    background: var(--bg-2);
    box-shadow: inset 2px 2px 0 var(--bevel-lo), inset -2px -2px 0 var(--bevel-hi);
  }
  .sc-favicon { width: 64px; height: 64px; image-rendering: pixelated; }
  .sc-creeper :global(.creeper) { width: 48px; height: 48px; }

  .sc-meta { flex: 1; min-width: 0; }
  .sc-row1 { display: flex; align-items: baseline; gap: 12px; }
  .sc-title { font-family: var(--font-head); font-weight: 600; font-size: 22px; white-space: nowrap; }
  .sc-ver { color: var(--dim); font-family: var(--font-mono); font-size: 17px; white-space: nowrap; }
  .sc-motd { margin: 6px 0 0; color: var(--muted); font-size: 15px; }
  .sc-motd .a { color: var(--accent); }
  /* mcstatus.io motd.html ships inline color styles; keep its spans inline. */
  .sc-motd-text :global(span) { display: inline; }

  .sc-state { text-align: right; flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
  .sc-pill {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 11px; font-family: var(--font-pixel, var(--font-mono));
    letter-spacing: 0.04em; text-transform: uppercase;
    padding: 3px 8px; color: var(--dim);
    box-shadow: inset 1px 1px 0 var(--bevel-hi), inset -1px -1px 0 var(--bevel-lo);
  }
  .sc-dot { width: 9px; height: 9px; background: var(--dim); image-rendering: pixelated; }
  .sc-pill[data-up="1"] { color: var(--accent-hi); }
  .sc-pill[data-up="1"] .sc-dot { background: var(--accent); box-shadow: 0 0 6px var(--glow); }
  .sc-pill[data-up="0"] { color: oklch(0.65 0.18 25); }
  .sc-pill[data-up="0"] .sc-dot { background: oklch(0.62 0.2 25); }
  .sc-count { font-variant-numeric: tabular-nums; font-size: 15px; color: var(--muted); }
  .sc-count b { color: var(--text); }

  .sc-bar { height: 6px; background: var(--bg-2); box-shadow: inset 1px 1px 0 var(--bevel-lo); }
  .sc-bar-fill { display: block; height: 100%; width: 0; background: var(--accent); transition: width 0.6s ease; }

  .sc-heads { display: flex; flex-wrap: wrap; gap: 6px; }
  .sc-heads img {
    width: 34px; height: 34px; image-rendering: pixelated;
    background: var(--bg-2);
    box-shadow: inset 1px 1px 0 var(--bevel-lo), inset -1px -1px 0 var(--bevel-hi);
  }
  .sc-empty { margin: 0; color: var(--dim); font-size: 13px; }
</style>

<script>
  const ZERO_UUID = "00000000-0000-0000-0000-000000000000";

  async function hydrate(el: HTMLElement) {
    const { api, avatar, mode, slots, online, offline, empty } = el.dataset;
    if (!api || !avatar || !mode) return;

    const q = <T extends HTMLElement>(s: string) => el.querySelector<T>(s);
    const pill = q(".sc-pill");
    const pillTxt = q(".sc-pill-txt");
    const favicon = q<HTMLImageElement>(".sc-favicon");
    const creeper = q(".sc-creeper");
    const onEl = q(".sc-on");
    const maxEl = q(".sc-max");
    const motdEl = q(".sc-motd-text");
    const bar = q(".sc-bar");
    const fill = q<HTMLElement>(".sc-bar-fill");
    const players = q(".sc-players");
    const heads = q(".sc-heads");
    const emptyEl = q(".sc-empty");

    let data: any;
    try {
      const r = await fetch(api, { headers: { Accept: "application/json" } });
      if (!r.ok) return; // keep SSG skeleton
      data = await r.json();
    } catch {
      return; // offline/network → keep SSG skeleton
    }

    const up = data.online === true;
    pill?.setAttribute("data-up", up ? "1" : "0");
    if (pillTxt) pillTxt.textContent = up ? (online ?? "Online") : (offline ?? "Offline");

    if (!up) return; // offline pill set; leave the rest as the static fallback

    // Live count + bar.
    const on = data.players?.online ?? 0;
    const max = data.players?.max ?? (Number(slots) || 0);
    if (onEl) onEl.textContent = String(on);
    if (maxEl) maxEl.textContent = String(max);
    if (bar && fill) {
      bar.hidden = false;
      fill.style.width = max > 0 ? `${Math.min(100, (on / max) * 100)}%` : "0%";
    }

    // Real server favicon.
    if (favicon && typeof data.icon === "string" && data.icon.startsWith("data:image")) {
      favicon.src = data.icon;
      favicon.hidden = false;
      creeper?.setAttribute("hidden", "");
    }

    // Color MOTD (mcstatus ships inline-styled spans for our own server).
    if (motdEl && data.motd?.html) motdEl.innerHTML = data.motd.html;

    // Player heads from OUR NMSR (Drasl skins). Filter promo/placeholder rows.
    const list: Array<{ uuid?: string; name_clean?: string }> = data.players?.list ?? [];
    const seen = new Set<string>();
    const real = list.filter((p) => {
      const u = (p.uuid ?? "").toLowerCase();
      if (!u || u === ZERO_UUID || seen.has(u)) return false;
      seen.add(u);
      return true;
    });
    if (players && heads) {
      players.hidden = false;
      if (real.length === 0) {
        if (emptyEl) {
          emptyEl.textContent = empty ?? "Nobody online.";
          emptyEl.hidden = false;
        }
      } else {
        heads.innerHTML = "";
        for (const p of real.slice(0, 16)) {
          const img = document.createElement("img");
          img.loading = "lazy";
          img.alt = p.name_clean ?? "";
          img.title = p.name_clean ?? "";
          img.src = `${avatar}/${mode}/${p.uuid}?size=64`;
          heads.appendChild(img);
        }
      }
    }
  }

  document.querySelectorAll<HTMLElement>(".servercard").forEach(hydrate);
</script>
