// ui.js — kleine DOM-Helfer, Toast, Badge, Escaping + Inline-Icon-Set.
// Bewusst kein Icon-Font / kein CDN: SVGs als Strings, faerben via currentColor.

export const $  = (s, r = document) => r.querySelector(s);
export const $$ = (s, r = document) => [...r.querySelectorAll(s)];

export function esc(s) {
  return String(s ?? "").replace(/[&<>]/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]));
}

let _tt;
export function toast(msg, err = false) {
  const t = $("#toast");
  if (!t) return;
  t.textContent = msg;
  t.className = "toast show" + (err ? " err" : "");
  clearTimeout(_tt);
  _tt = setTimeout(() => (t.className = "toast"), 3200);
}

// Modell-Status -> Badge-HTML
export function badge(state, progress) {
  if (state === "running" || state === "ready")    return '<span class="badge b-run">geladen</span>';
  if (state === "loading" || state === "starting") return '<span class="badge b-load">lädt…</span>';
  if (state === "downloading") {
    const pct = progress != null ? ` ${Math.round(progress)}%` : "";
    return `<span class="badge b-load">↓ Download${pct}</span>`;
  }
  return '<span class="badge b-run">bereit</span>';
}

// relative Zeit aus Unix-Sekunden (z.B. "2m", "13h")
export function ago(ts) {
  if (!ts) return "";
  const s = Math.max(0, Math.floor(Date.now() / 1000 - ts));
  if (s < 60)    return s + "s";
  if (s < 3600)  return Math.floor(s / 60) + "m";
  if (s < 86400) return Math.floor(s / 3600) + "h";
  return Math.floor(s / 86400) + "d";
}

const _svg = (p) =>
  `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" ` +
  `stroke-linecap="round" stroke-linejoin="round">${p}</svg>`;

// Icon-Set (Stroke-Style, an die Referenz angelehnt)
export const ICON = {
  logo:     _svg('<circle cx="12" cy="12" r="3.2"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M19 5l-2 2M7 17l-2 2"/>'),
  grid:     _svg('<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>'),
  cpu:      _svg('<rect x="6" y="6" width="12" height="12" rx="2"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3"/>'),
  pulse:    _svg('<path d="M3 12h4l2 6 4-14 2 8h6"/>'),
  server:   _svg('<rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><path d="M7 7.5h.01M7 16.5h.01"/>'),
  book:     _svg('<path d="M4 5a2 2 0 0 1 2-2h13v16H6a2 2 0 0 0-2 2z"/><path d="M19 19H6a2 2 0 0 0-2 2"/>'),
  help:     _svg('<circle cx="12" cy="12" r="9"/><path d="M9.5 9.2a2.5 2.5 0 0 1 4.8 1c0 1.7-2.3 2-2.3 3.4"/><path d="M12 17h.01"/>'),
  settings: _svg('<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 1 1-4 0v-.1A1.6 1.6 0 0 0 6.6 19l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0-1.1-2.7H2a2 2 0 1 1 0-4h.1A1.6 1.6 0 0 0 4 6.6l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1A1.6 1.6 0 0 0 9 4.6V4a2 2 0 1 1 4 0v.1A1.6 1.6 0 0 0 17.4 6l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0 1.1 2.7H21a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1z"/>'),
  swap:     _svg('<path d="M7 4 3 8l4 4"/><path d="M3 8h12a4 4 0 0 1 0 8h-1"/><path d="m17 20 4-4-4-4"/>'),
  monitor:  _svg('<rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 20h8M12 16v4"/>'),
  layers:   _svg('<path d="m12 2 9 5-9 5-9-5z"/><path d="m3 12 9 5 9-5M3 17l9 5 9-5"/>'),
  alert:    _svg('<path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/><path d="M12 9v4M12 17h.01"/>'),
  gauge:    _svg('<path d="M12 14 16 9"/><circle cx="12" cy="13" r="9"/><path d="M12 4v2M21 13h-2M5 13H3"/>'),
  search:   _svg('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>'),
  compass:  _svg('<circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/>'),
  code:     _svg('<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>'),
  eye:      _svg('<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>'),
  refresh:  _svg('<path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/>'),
  file:     _svg('<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/><path d="M9 13h6M9 17h5"/>'),
  database: _svg('<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5"/><path d="M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"/>'),
  thermo:   _svg('<path d="M14 14.76V5a2 2 0 0 0-4 0v9.76a4 4 0 1 0 4 0z"/>'),
  shield:   _svg('<path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z"/><path d="m9 12 2 2 4-4"/>'),
  info:     _svg('<circle cx="12" cy="12" r="9"/><path d="M12 16v-4M12 8h.01"/>'),
  chevron:  _svg('<path d="m9 6 6 6-6 6"/>'),
  bolt:     _svg('<path d="M13 2 3 14h7l-1 8 10-12h-7z"/>'),
  x:        _svg('<path d="M18 6 6 18M6 6l12 12"/>'),
  check:    _svg('<path d="M20 6 9 17l-5-5"/>'),
  download: _svg('<path d="M12 3v12M7 10l5 5 5-5"/><path d="M5 21h14"/>'),
  clock:    _svg('<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>'),
};

export function icon(name) { return ICON[name] || ""; }

// kleines Info-ⓘ mit Klartext-Tooltip (native title) — für Erklärungen direkt am Feld.
export function infoDot(text) {
  return `<span class="info-dot" tabindex="0" title="${esc(text)}" aria-label="${esc(text)}">${ICON.info}</span>`;
}

// ---------------------------------------------------------------------------
// Format-Helfer (Klartext-Zahlen)
// ---------------------------------------------------------------------------
export function fmtBytes(b) {
  if (!b && b !== 0) return "–";
  const gb = b / 1024 / 1024 / 1024;
  if (gb >= 1) return gb.toFixed(1) + " GB";
  return Math.round(b / 1024 / 1024) + " MB";
}
export function fmtPct(n) { return Math.round(n || 0) + " %"; }

// ---------------------------------------------------------------------------
// Bestätigungs-Dialog (ersetzt nacktes window.confirm) — gibt Promise<boolean>.
// Für heikle Aktionen mit Klartext-Konsequenz.
// ---------------------------------------------------------------------------
export function confirmModal({ title, body, confirmLabel = "Bestätigen", cancelLabel = "Abbrechen", danger = false }) {
  return new Promise(resolve => {
    const ov = document.createElement("div");
    ov.className = "modal-overlay";
    ov.innerHTML = `<div class="modal-card" role="dialog" aria-modal="true">
      <h3>${esc(title)}</h3>
      <p>${body}</p>
      <div class="modal-actions">
        <button data-x="0">${esc(cancelLabel)}</button>
        <button class="${danger ? "danger" : "primary"}" data-x="1">${esc(confirmLabel)}</button>
      </div></div>`;
    const done = v => { ov.remove(); resolve(v); };
    ov.addEventListener("click", e => {
      if (e.target === ov) return done(false);
      const b = e.target.closest("[data-x]");
      if (b) done(b.getAttribute("data-x") === "1");
    });
    document.body.appendChild(ov);
    ov.querySelector('[data-x="1"]').focus();
  });
}

// ---------------------------------------------------------------------------
// Eingabe-Dialog (ersetzt window.prompt) — gibt Promise<string|null>.
// password:true blendet die Eingabe aus (für sudo-Passwort).
// ---------------------------------------------------------------------------
export function promptModal({ title, body = "", placeholder = "", password = false, confirmLabel = "Weiter", danger = false }) {
  return new Promise(resolve => {
    const ov = document.createElement("div");
    ov.className = "modal-overlay";
    ov.innerHTML = `<div class="modal-card" role="dialog" aria-modal="true">
      <h3>${esc(title)}</h3>
      ${body ? `<p>${body}</p>` : ""}
      <input type="${password ? "password" : "text"}" placeholder="${esc(placeholder)}" autocomplete="off" style="margin-bottom:18px">
      <div class="modal-actions">
        <button data-x="0">Abbrechen</button>
        <button class="${danger ? "danger" : "primary"}" data-x="1">${esc(confirmLabel)}</button>
      </div></div>`;
    const inp = ov.querySelector("input");
    const done = v => { ov.remove(); resolve(v); };
    ov.addEventListener("click", e => {
      if (e.target === ov) return done(null);
      const b = e.target.closest("[data-x]");
      if (b) done(b.getAttribute("data-x") === "1" ? (inp.value || "") : null);
    });
    inp.addEventListener("keydown", e => { if (e.key === "Enter") done(inp.value || ""); if (e.key === "Escape") done(null); });
    document.body.appendChild(ov);
    inp.focus();
  });
}
