// Shared UI primitives for zlap-termin
// Exposes to window: Icon, Dot, StatusBadge, LevelBadge, Sparkline, RelTime,
// TickingSince, fmt, useInterval, useNow, cls

const { useState, useEffect, useRef, useMemo, useCallback } = React;

function cls(...args) {
  return args.filter(Boolean).join(" ");
}

/* ---------- Minimal SVG icon set (no external lib) ---------- */
const ICONS = {
  activity: "M22 12h-4l-3 9L9 3l-3 9H2",
  settings: "M12 1v6m0 10v6m11-11h-6M7 12H1m17.5-6.5l-4.2 4.2M9.7 14.3l-4.2 4.2m0-12.7l4.2 4.2m4.6 4.6l4.2 4.2",
  list: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01",
  terminal: "M4 17l6-6-6-6m8 14h8",
  bell: "M6 8a6 6 0 0112 0c0 7 3 9 3 9H3s3-2 3-9m3 13a3 3 0 006 0",
  play: "M5 3l14 9-14 9V3z",
  pause: "M6 4h4v16H6zM14 4h4v16h-4z",
  restart: "M3 12a9 9 0 0115-6.7L21 8M21 3v5h-5M21 12a9 9 0 01-15 6.7L3 16m0 5v-5h5",
  search: "M11 19a8 8 0 100-16 8 8 0 000 16zM21 21l-4.3-4.3",
  external: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3",
  check: "M20 6L9 17l-5-5",
  x: "M18 6L6 18M6 6l12 12",
  chevDown: "M6 9l6 6 6-6",
  chevRight: "M9 6l6 6-6 6",
  chevLeft: "M15 6l-6 6 6 6",
  chevUp: "M18 15l-6-6-6 6",
  filter: "M22 3H2l8 9.5V19l4 2v-8.5L22 3z",
  eye: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8S1 12 1 12zM12 15a3 3 0 100-6 3 3 0 000 6z",
  eyeOff: "M17.94 17.94A10.94 10.94 0 0112 20c-7 0-11-8-11-8a20.16 20.16 0 015.06-6.06M9.9 4.24A10.94 10.94 0 0112 4c7 0 11 8 11 8a20.16 20.16 0 01-3.17 4.18M1 1l22 22M14.12 14.12A3 3 0 119.88 9.88",
  mail: "M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2zM22 6l-10 7L2 6",
  send: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z",
  calendar: "M3 4h18v18H3zM3 10h18M8 2v4M16 2v4",
  clock: "M12 2a10 10 0 100 20 10 10 0 000-20zM12 6v6l4 2",
  shield: "M12 2l9 4v6c0 5-4 9-9 10-5-1-9-5-9-10V6l9-4z",
  logout: "M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4M16 17l5-5-5-5M21 12H9",
  user: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z",
  moon: "M21 12.8A9 9 0 1111.2 3a7 7 0 009.8 9.8z",
  sun: "M12 5V1M12 23v-4M4.2 4.2l2.9 2.9M16.9 16.9l2.9 2.9M1 12h4M19 12h4M4.2 19.8l2.9-2.9M16.9 7.1l2.9-2.9M12 17a5 5 0 100-10 5 5 0 000 10z",
  menu: "M3 6h18M3 12h18M3 18h18",
  zap: "M13 2L3 14h9l-1 8 10-12h-9l1-8z",
  trash: "M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6",
  info: "M12 2a10 10 0 100 20 10 10 0 000-20zM12 16v-4M12 8h.01",
  alert: "M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0zM12 9v4M12 17h.01",
  link: "M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.72M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71",
  copy: "M20 9h-9a2 2 0 00-2 2v9a2 2 0 002 2h9a2 2 0 002-2v-9a2 2 0 00-2-2zM5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1",
  download: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3",
};

function Icon({ name, size = 14, stroke = 1.6, className = "", style = {} }) {
  const d = ICONS[name];
  if (!d) return null;
  return (
    <svg width={size} height={size} viewBox="0 0 24 24"
         fill="none" stroke="currentColor" strokeWidth={stroke}
         strokeLinecap="round" strokeLinejoin="round"
         className={className} style={style} aria-hidden="true">
      <path d={d} />
    </svg>
  );
}

function Dot({ color = "var(--ok)", pulse = false, size = 8 }) {
  return (
    <span className={"dot" + (pulse ? " pulse" : "")}
          style={{ color, width: size, height: size }} />
  );
}

function StatusBadge({ state }) {
  // state: 'online' | 'offline' | 'rate' | 'paused'
  const map = {
    online: { label: "ONLINE",       cls: "ok",   color: "var(--ok)",   pulse: true  },
    offline:{ label: "OFFLINE",      cls: "err",  color: "var(--err)",  pulse: false },
    rate:   { label: "RATE LIMITED", cls: "warn", color: "var(--warn)", pulse: true  },
    paused: { label: "PAUSED",       cls: "muted",color: "var(--fg-muted)", pulse: false },
  };
  const m = map[state] || map.offline;
  return (
    <span className={"badge " + m.cls}>
      <Dot color={m.color} pulse={m.pulse} />
      {m.label}
    </span>
  );
}

function LevelBadge({ level }) {
  const cls = level === "ERROR" ? "err" : level === "WARN" ? "warn" : "info";
  return <span className={"badge " + cls} style={{ minWidth: 52, justifyContent: "center" }}>{level}</span>;
}

/* ---------- Time utils ---------- */
const fmt = {
  hms(d) {
    const p = (n) => String(n).padStart(2, "0");
    return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
  },
  hm(d) {
    const p = (n) => String(n).padStart(2, "0");
    return `${p(d.getHours())}:${p(d.getMinutes())}`;
  },
  ymd(d) {
    const p = (n) => String(n).padStart(2, "0");
    return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`;
  },
  full(d) {
    return `${fmt.ymd(d)} ${fmt.hms(d)}`;
  },
  rel(from, to = new Date()) {
    const diff = Math.max(0, Math.floor((to - from) / 1000));
    if (diff < 5) return "przed chwilą";
    if (diff < 60) return `${diff} s temu`;
    if (diff < 3600) return `${Math.floor(diff/60)} min temu`;
    if (diff < 86400) return `${Math.floor(diff/3600)} h temu`;
    const d = Math.floor(diff/86400);
    return `${d} d temu`;
  },
  durShort(seconds) {
    const s = Math.max(0, Math.floor(seconds));
    if (s < 60) return `${s}s`;
    const m = Math.floor(s/60);
    const sec = s % 60;
    if (m < 60) return `${m}m ${sec}s`;
    const h = Math.floor(m/60);
    return `${h}h ${m % 60}m`;
  }
};

function useInterval(fn, delay) {
  const saved = useRef(fn);
  useEffect(() => { saved.current = fn; }, [fn]);
  useEffect(() => {
    if (delay == null) return;
    const id = setInterval(() => saved.current(), delay);
    return () => clearInterval(id);
  }, [delay]);
}

function useNow(tickMs = 1000) {
  const [n, setN] = useState(() => new Date());
  useInterval(() => setN(new Date()), tickMs);
  return n;
}

function RelTime({ date }) {
  const now = useNow(1000);
  return <span className="mono">{fmt.rel(date, now)}</span>;
}

function TickingSince({ date, prefix = "", live = true }) {
  const now = useNow(live ? 1000 : null);
  const diff = Math.max(0, Math.floor((now - date) / 1000));
  return <span className="mono tnum">{prefix}{fmt.durShort(diff)}</span>;
}

/* ---------- Tiny sparkline ---------- */
function Sparkline({ values, width = 120, height = 32, color = "var(--info)", fill = true }) {
  if (!values || values.length === 0) return null;
  const max = Math.max(...values, 1);
  const min = Math.min(...values, 0);
  const step = width / (values.length - 1 || 1);
  const y = (v) => height - 4 - ((v - min) / (max - min || 1)) * (height - 8);
  const points = values.map((v, i) => `${i * step},${y(v)}`).join(" ");
  const path = "M " + points.replace(/ /g, " L ");
  const area = `M 0,${height} L ${points.replace(/ /g, " L ")} L ${width},${height} Z`;
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: "block" }}>
      {fill && <path d={area} fill={color} opacity="0.12" />}
      <path d={path} fill="none" stroke={color} strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

Object.assign(window, {
  cls, Icon, Dot, StatusBadge, LevelBadge, Sparkline,
  RelTime, TickingSince, fmt, useInterval, useNow,
});
