// Shared formatters and small UI atoms used across the prototype.

const money = (n) => "Rp " + (Number(n) || 0).toLocaleString("id-ID");
const moneyK = (n) => {
  const v = Number(n) || 0;
  if (v >= 1000000) return (v / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
  if (v >= 1000) return Math.round(v / 1000) + "K";
  return String(v);
};
// Normalize a timestamp (Firestore Timestamp, Date, number, or ISO string) to ms.
const tsToMs = (t) => {
  if (t == null) return 0;
  if (typeof t === "number") return t;
  if (t instanceof Date) return t.getTime();
  if (typeof t.toMillis === "function") return t.toMillis();
  if (typeof t.toDate === "function") return t.toDate().getTime();
  if (typeof t.seconds === "number") return t.seconds * 1000 + Math.floor((t.nanoseconds || 0) / 1e6);
  const n = new Date(t).getTime();
  return isNaN(n) ? 0 : n;
};
const timeSince = (ts) => {
  const ms = tsToMs(ts);
  if (!ms) return "—";
  const s = Math.max(0, Math.floor((Date.now() - ms) / 1000));
  if (s < 60) return s + "s";
  const m = Math.floor(s / 60);
  if (m < 60) return m + "m " + (s % 60).toString().padStart(2, "0") + "s";
  const h = Math.floor(m / 60);
  return h + "h " + (m % 60) + "m";
};
const DEFAULT_TZ = "Asia/Jakarta"; // GMT+7
// The active timezone is driven by Admin → Settings (settings/general doc),
// applied at runtime to window.APP_TZ by store.jsx. Falls back to GMT+7.
const appTZ = () => window.APP_TZ || DEFAULT_TZ;
const fmtClock = (d = new Date()) =>
  d.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", timeZone: appTZ() });
const fmtDay = (d = new Date()) =>
  d.toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long", timeZone: appTZ() });
// YYYY-MM-DD for the calendar day in the configured timezone. Used as the
// per-day grouping key for orders/shifts so "today" means local 00:00–24:00,
// not UTC midnight.
const dayKey = (d = new Date()) =>
  new Intl.DateTimeFormat("en-CA", {
    timeZone: appTZ(), year: "numeric", month: "2-digit", day: "2-digit",
  }).format(d);

// useTick: forces a re-render every `ms` (for live timers)
function useTick(ms = 1000) {
  const [, set] = React.useState(0);
  React.useEffect(() => {
    const t = setInterval(() => set((x) => x + 1), ms);
    return () => clearInterval(t);
  }, [ms]);
}

// ProductSwatch — a placeholder illustration based on a single color.
// Subtle ring + stripes; never an SVG of an actual cup.
function ProductSwatch({ color, label, size = 48 }) {
  const initials = (label || "")
    .split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
  return (
    <div
      className="swatch"
      style={{
        width: size, height: size,
        background: `radial-gradient(circle at 30% 28%, ${color}cc, ${color} 60%, ${color}aa)`,
      }}
    >
      <span className="swatch-rim" />
      <span className="swatch-initials" style={{ fontSize: size * 0.32 }}>{initials}</span>
    </div>
  );
}

// StatusPill — minimal status tag.
function StatusPill({ status, children }) {
  return <span className={`pill pill-${status}`}>{children || status}</span>;
}

// Mono number — for prices, IDs, KPIs
function Mono({ children, className = "" }) {
  return <span className={`mono ${className}`}>{children}</span>;
}

// Escape text for the printable-slip HTML templates (admin.jsx,
// kitchen.jsx). Pre-booking fields (customer name, phone, item notes) come
// from anonymous customers on /prebook, so they must never reach innerHTML
// unescaped — that would run their markup inside a staff session.
function escHtml(v) {
  return String(v == null ? "" : v)
    .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}

Object.assign(window, {
  money, moneyK, timeSince, tsToMs, fmtClock, fmtDay, dayKey, appTZ, DEFAULT_TZ, useTick,
  ProductSwatch, StatusPill, Mono, escHtml,
});
