// ====== Shared data ======

// Shared confirmation dialog — renders via portal, used across all pages
const ConfirmDialog = ({ title, message, confirmLabel = "Confirm", cancelLabel = "Cancel", onConfirm, onCancel, zIndex = 500 }) =>
  ReactDOM.createPortal(
    <div style={{ position: "fixed", inset: 0, zIndex, display: "flex", alignItems: "center", justifyContent: "center", background: "rgba(17,38,27,0.45)", backdropFilter: "blur(2px)" }}
      onClick={onCancel}>
      <div style={{ background: "var(--cream-50)", borderRadius: "var(--r-lg)", boxShadow: "0 8px 40px rgba(0,0,0,0.22)", padding: "28px 28px 20px", maxWidth: 360, width: "90%", border: "1px solid var(--line-gold)" }}
        onClick={e => e.stopPropagation()}>
        <div style={{ fontFamily: "var(--font-display)", fontSize: 22, color: "var(--green-800)", marginBottom: 10 }}>{title}</div>
        <div style={{ fontSize: 13.5, color: "var(--ink-600)", lineHeight: 1.55, marginBottom: 22 }}>{message}</div>
        <div style={{ display: "flex", gap: 10, justifyContent: "flex-end" }}>
          <button className="btn btn-ghost" onClick={onCancel}>{cancelLabel}</button>
          <button className="btn btn-primary" style={{ background: "var(--rose-500)", borderColor: "var(--rose-500)" }} onClick={onConfirm}>{confirmLabel}</button>
        </div>
      </div>
    </div>,
    document.body
  );

const _viMap = {'à':'a','á':'a','ả':'a','ã':'a','ạ':'a','ă':'a','ằ':'a','ắ':'a','ẳ':'a','ẵ':'a','ặ':'a','â':'a','ầ':'a','ấ':'a','ẩ':'a','ẫ':'a','ậ':'a','è':'e','é':'e','ẻ':'e','ẽ':'e','ẹ':'e','ê':'e','ề':'e','ế':'e','ể':'e','ễ':'e','ệ':'e','ì':'i','í':'i','ỉ':'i','ĩ':'i','ị':'i','ò':'o','ó':'o','ỏ':'o','õ':'o','ọ':'o','ô':'o','ồ':'o','ố':'o','ổ':'o','ỗ':'o','ộ':'o','ơ':'o','ờ':'o','ớ':'o','ở':'o','ỡ':'o','ợ':'o','ù':'u','ú':'u','ủ':'u','ũ':'u','ụ':'u','ư':'u','ừ':'u','ứ':'u','ử':'u','ữ':'u','ự':'u','ỳ':'y','ý':'y','ỷ':'y','ỹ':'y','ỵ':'y','đ':'d'};
const normalizeVi = (s) => (s || "")
  .toString()
  .normalize("NFD")
  .replace(/[\u0300-\u036f]/g, "")
  .replace(/[đĐ]/g, "d")
  .toLowerCase()
  .split("")
  .map(c => _viMap[c] || c)
  .join("");

const MenuCachedImage = ({ itemId, imageUrl, alt = "", style, className, loading = "lazy", decoding = "async" }) => {
  const [src, setSrc] = React.useState("");
  const [failedCacheUrl, setFailedCacheUrl] = React.useState("");
  const [retryVersion, setRetryVersion] = React.useState(0);

  React.useEffect(() => {
    let cancelled = false;
    let objectUrl = null;
    setFailedCacheUrl("");
    setSrc("");

    if (!imageUrl) {
      return undefined;
    }

    const canRenderImage = (url) => new Promise((resolve) => {
      const image = new Image();
      image.onload = async () => {
        try {
          await image.decode?.();
        } catch {}
        resolve(Boolean(image.naturalWidth || image.width));
      };
      image.onerror = () => resolve(false);
      image.src = url;
    });

    const load = async () => {
      try {
        const cachedUrl = await window.TeaGangBackend?.getCachedMenuImageUrl?.(itemId, imageUrl);
        if (cancelled) {
          if (cachedUrl?.startsWith?.("blob:")) URL.revokeObjectURL(cachedUrl);
          return;
        }
        if (!cachedUrl || !cachedUrl.startsWith?.("blob:")) return;
        const valid = await canRenderImage(cachedUrl);
        if (cancelled) {
          if (cachedUrl?.startsWith?.("blob:")) URL.revokeObjectURL(cachedUrl);
          return;
        }
        if (!valid) {
          if (cachedUrl?.startsWith?.("blob:")) URL.revokeObjectURL(cachedUrl);
          await window.TeaGangBackend?.clearCachedMenuImage?.(itemId, imageUrl);
          return;
        }
        objectUrl = cachedUrl.startsWith("blob:") ? cachedUrl : null;
        setSrc(cachedUrl);
      } catch (error) {
        console.warn("Could not load cached menu image.", error);
        if (!cancelled) setSrc("");
      }
    };

    load();
    return () => {
      cancelled = true;
      if (objectUrl) URL.revokeObjectURL(objectUrl);
    };
  }, [itemId, imageUrl, retryVersion]);

  if (!src) return null;
  return (
    <img
      src={src}
      alt={alt}
      className={className}
      loading={loading}
      decoding={decoding}
      style={style}
      onError={async () => {
        if (src?.startsWith?.("blob:") && failedCacheUrl !== src) {
          setFailedCacheUrl(src);
          setSrc("");
          try {
            await window.TeaGangBackend?.clearCachedMenuImage?.(itemId, imageUrl);
            setRetryVersion(value => value + 1);
          } catch (error) {
            console.warn("Could not clear cached menu image.", error);
          }
        }
      }}
    />
  );
};

const formatPhone = (value) => {
  const d = (value || "").replace(/\D/g, "").slice(0, 10);
  if (d.length === 0) return "";
  if (d.length <= 3) return `(${d}`;
  if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;
  return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
};
const MENU_ITEMS = [
  // Milk Teas
  { id: "m01", cat: "milk-tea", name: "Brown Sugar Pearl", price: 6.25, glyph: "黒", desc: "Roasted brown sugar, hand-pulled pearls, fresh milk.", tag: "Signature", available: true, sold: 412 },
  { id: "m02", cat: "milk-tea", name: "Hokkaido Royal", price: 6.75, glyph: "雪", desc: "Hokkaido milk base with assam black tea.", tag: "Bestseller", available: true, sold: 388 },
  { id: "m03", cat: "milk-tea", name: "Thai Tea Latte", price: 5.75, glyph: "暹", desc: "Star anise & cardamom-spiced thai tea, condensed milk.", available: true, sold: 256 },
  { id: "m04", cat: "milk-tea", name: "Taro Cloud", price: 6.50, glyph: "芋", desc: "Stone-ground taro, oat milk, soft cream foam.", tag: "New", available: true, sold: 201 },
  { id: "m05", cat: "milk-tea", name: "Matcha Velvet", price: 6.95, glyph: "抹", desc: "Ceremonial Uji matcha whisked with house milk.", available: true, sold: 332 },
  // Fruit Teas
  { id: "f01", cat: "fruit-tea", name: "Peach Oolong", price: 5.95, glyph: "桃", desc: "Roasted oolong, ripe yellow peach, jasmine perfume.", available: true, sold: 178 },
  { id: "f02", cat: "fruit-tea", name: "Lychee Dragonfruit", price: 6.25, glyph: "荔", desc: "Lychee, dragonfruit, white tea, basil seeds.", tag: "New", available: true, sold: 145 },
  { id: "f03", cat: "fruit-tea", name: "Yuzu Green", price: 5.75, glyph: "柚", desc: "Sencha, yuzu zest, honey.", available: false, sold: 92 },
  // Pure tea
  { id: "p01", cat: "pure", name: "Phoenix Dancong", price: 4.95, glyph: "鳳", desc: "Single-origin oolong, honey-orchid finish.", available: true, sold: 64 },
  { id: "p02", cat: "pure", name: "Jasmine Silver Needle", price: 5.50, glyph: "茉", desc: "Hand-picked white tea, jasmine layered eight times.", tag: "Limited", available: true, sold: 38 },
  // Snacks
  { id: "s01", cat: "snacks", name: "Mochi Trio", price: 4.50, glyph: "餅", desc: "Strawberry, matcha, black sesame mochi.", available: true, sold: 156 },
  { id: "s02", cat: "snacks", name: "Pineapple Cake", price: 3.25, glyph: "鳳", desc: "Buttery shortcrust, candied pineapple paste.", available: true, sold: 122 },
];

const CATEGORIES = [
  { id: "all", label: "All", count: MENU_ITEMS.length },
  { id: "milk-tea", label: "Milk Tea", count: MENU_ITEMS.filter(m => m.cat === "milk-tea").length },
  { id: "fruit-tea", label: "Fruit Tea", count: MENU_ITEMS.filter(m => m.cat === "fruit-tea").length },
  { id: "pure", label: "Pure Tea", count: MENU_ITEMS.filter(m => m.cat === "pure").length },
  { id: "snacks", label: "Snacks", count: MENU_ITEMS.filter(m => m.cat === "snacks").length },
];

const ORDERS = [
  {
    id: "TG-4218", order_number: 4218, customer: "Amelia Chen", phone: "(415) 555-0142", time: "2 min ago", waited: 2,
    channel: "pickup", source: "In-app", total: 18.45, status: "pending", note: "",
    items: [
      { name: "Brown Sugar Pearl", qty: 1, price: 6.25, mods: "Large · 50% sugar · Less ice", glyph: "黒" },
      { name: "Matcha Velvet", qty: 1, price: 6.95, mods: "Regular · 25% sugar · Oat milk +0.75", glyph: "抹" },
      { name: "Mochi Trio", qty: 1, price: 4.50, mods: "", glyph: "餅" },
    ],
  },
  {
    id: "TG-4217", order_number: 4217, customer: "Marcus Park", phone: "(415) 555-0188", time: "4 min ago", waited: 4,
    channel: "pickup", source: "Counter", total: 12.95, status: "pending", note: "Allergic to peanuts",
    items: [
      { name: "Hokkaido Royal", qty: 1, price: 6.75, mods: "Large · 75% sugar · Boba", glyph: "雪" },
      { name: "Peach Oolong", qty: 1, price: 5.95, mods: "Regular · No ice · Aloe", glyph: "桃" },
    ],
  },
  {
    id: "TG-4216", order_number: 4216, customer: "Sofia Reyes", phone: "(415) 555-0203", time: "6 min ago", waited: 6,
    channel: "pickup", source: "Counter", total: 6.50, status: "pending", note: "",
    items: [{ name: "Taro Cloud", qty: 1, price: 6.50, mods: "Large · 100% sugar · Cream foam", glyph: "芋" }],
  },
  {
    id: "TG-4215", order_number: 4215, customer: "Jamal Wright", phone: "(415) 555-0177", time: "7 min ago", waited: 7,
    channel: "delivery", source: "Uber Eats", total: 24.20, status: "pending", note: "Leave at door",
    items: [
      { name: "Thai Tea Latte", qty: 2, price: 5.75, mods: "Regular · 50% sugar", glyph: "暹" },
      { name: "Lychee Dragonfruit", qty: 1, price: 6.25, mods: "Large · 25% sugar · Basil seeds", glyph: "荔" },
      { name: "Pineapple Cake", qty: 2, price: 3.25, mods: "", glyph: "鳳" },
    ],
  },
  {
    id: "TG-4213", order_number: 4213, customer: "Daniel Liu", phone: "(415) 555-0119", time: "12 min ago", waited: 12,
    channel: "pickup", source: "In-app", total: 13.70, status: "ready", note: "",
    items: [
      { name: "Brown Sugar Pearl", qty: 2, price: 6.25, mods: "Large · 50% sugar", glyph: "黒" },
    ],
  },
  {
    id: "TG-4212", order_number: 4212, customer: "Hana Yamamoto", phone: "(415) 555-0166", time: "15 min ago", waited: 15,
    channel: "delivery", source: "DoorDash", total: 19.40, status: "ready", note: "",
    items: [
      { name: "Matcha Velvet", qty: 1, price: 6.95, mods: "Hot · 50% sugar", glyph: "抹" },
      { name: "Yuzu Green", qty: 1, price: 5.75, mods: "Regular · No ice", glyph: "柚" },
      { name: "Mochi Trio", qty: 1, price: 4.50, mods: "", glyph: "餅" },
    ],
  },
  {
    id: "TG-4214", order_number: 4214, customer: "Priya Shah", phone: "(415) 555-0151", time: "9 min ago", waited: 9,
    channel: "pickup", source: "Phone-in", total: 13.20, status: "ready", note: "Stir well",
    items: [
      { name: "Jasmine Silver Needle", qty: 1, price: 5.50, mods: "Hot · Pure", glyph: "茉" },
      { name: "Mochi Trio", qty: 1, price: 4.50, mods: "", glyph: "餅" },
      { name: "Pineapple Cake", qty: 1, price: 3.25, mods: "", glyph: "鳳" },
    ],
  },
  {
    id: "TG-4211", order_number: 4211, customer: "Owen Bauer", phone: "(415) 555-0134", time: "21 min ago", waited: 21,
    channel: "pickup", source: "Counter", total: 6.75, status: "completed", note: "",
    payment: { method: "card", brand: "Visa", last4: "4218", tip: 1.00 },
    items: [{ name: "Hokkaido Royal", qty: 1, price: 6.75, mods: "Regular · 75% sugar", glyph: "雪" }],
  },
  {
    id: "TG-4210", order_number: 4210, customer: "Mei Tanaka", phone: "(415) 555-0122", time: "23 min ago", waited: 23,
    channel: "pickup", source: "In-app", total: 11.45, status: "completed", note: "",
    payment: { method: "apple_pay", tip: 2.00 },
    items: [
      { name: "Phoenix Dancong", qty: 1, price: 4.95, mods: "Hot", glyph: "鳳" },
      { name: "Pineapple Cake", qty: 2, price: 3.25, mods: "", glyph: "鳳" },
    ],
  },
];

// ====== Drink customization options (from store_drink_options + size_options) ======
const SIZE_OPTIONS = [
  { label: "Regular", delta: 0 },
  { label: "Large",   delta: 0.75 },
];

const FEED = [
  { t: "11:42", dot: "green", text: "Order TG-4218 placed — Amelia C.", amt: "$18.45" },
  { t: "11:40", dot: "gold", text: "Low stock alert: Brown sugar syrup (12% left)", amt: "" },
  { t: "11:38", dot: "green", text: "Order TG-4217 placed — Marcus P.", amt: "$12.95" },
  { t: "11:34", dot: "green", text: "Order TG-4216 placed — Sofia R.", amt: "$6.50" },
  { t: "11:30", dot: "gold", text: "Promo \"Afternoon Steep\" activated for 3h window", amt: "" },
  { t: "11:24", dot: "rose", text: "Order TG-4209 refunded — wrong sugar level", amt: "−$7.25" },
  { t: "11:18", dot: "green", text: "Shift change: Yuki started · Hana on break", amt: "" },
];
const SWEETNESS_LEVELS = [
  { label: "0%",   value: 0 },
  { label: "25%",  value: 25 },
  { label: "50%",  value: 50, isDefault: true },
  { label: "75%",  value: 75 },
  { label: "100%", value: 100 },
];
const ICE_LEVELS = [
  { label: "No ice",   value: "none" },
  { label: "Less ice", value: "less" },
  { label: "Regular",  value: "regular", isDefault: true },
  { label: "Hot",      value: "hot" },
];
const ADDONS = [
  { id: "boba",    name: "Tapioca pearls", price: 0.75 },
  { id: "lychee",  name: "Lychee jelly",   price: 0.75 },
  { id: "aloe",    name: "Aloe vera",      price: 0.75 },
  { id: "cheese",  name: "Cheese foam",    price: 1.25 },
  { id: "oat",     name: "Oat milk",       price: 0.75 },
  { id: "espresso",name: "Espresso shot",  price: 1.50 },
];
const ORDER_SOURCES = ["Counter", "In-app", "Phone-in", "Uber Eats", "DoorDash"];

// ====== Inventory (ingredients & supplies) ======
const INVENTORY = [
  { id: "i01", name: "Tapioca pearls (black)", cat: "Toppings",  unit: "kg",  stock: 4.2,  par: 24, daily: 2.8, supplier: "Pearl Bros.",   last: "Mon · 2d", glyph: "黑" },
  { id: "i02", name: "Brown sugar syrup",      cat: "Syrups",    unit: "L",   stock: 1.4,  par: 12, daily: 1.8, supplier: "Sugarcane Co.", last: "Sun · 3d", glyph: "糖" },
  { id: "i03", name: "Hokkaido whole milk",    cat: "Dairy",     unit: "L",   stock: 6.0,  par: 20, daily: 4.5, supplier: "Daily Dairy",   last: "Today",     glyph: "乳" },
  { id: "i04", name: "Ceremonial matcha",      cat: "Tea",       unit: "g",   stock: 320,  par: 800, daily: 60, supplier: "Uji Tea House",last: "Wed · 5d", glyph: "抹" },
  { id: "i05", name: "Phoenix oolong leaves",  cat: "Tea",       unit: "g",   stock: 480,  par: 1000,daily: 75, supplier: "Wuyi Imports", last: "Wed · 5d", glyph: "鳳" },
  { id: "i06", name: "Jasmine silver needle",  cat: "Tea",       unit: "g",   stock: 220,  par: 500, daily: 25, supplier: "Wuyi Imports", last: "Thu · 6d", glyph: "茉" },
  { id: "i07", name: "Assam black tea",        cat: "Tea",       unit: "g",   stock: 640,  par: 1200,daily: 90, supplier: "Wuyi Imports", last: "Mon · 2d", glyph: "黑" },
  { id: "i08", name: "Lychee purée",           cat: "Fruit",     unit: "L",   stock: 3.6,  par: 8,   daily: 1.1,supplier: "Sun Orchards",  last: "Tue · 4d", glyph: "荔" },
  { id: "i09", name: "Yellow peach",           cat: "Fruit",     unit: "kg",  stock: 5.8,  par: 10,  daily: 1.4,supplier: "Sun Orchards",  last: "Today",    glyph: "桃" },
  { id: "i10", name: "Yuzu zest",              cat: "Fruit",     unit: "g",   stock: 180,  par: 400, daily: 40, supplier: "Sun Orchards",  last: "Tue · 4d", glyph: "柚" },
  { id: "i11", name: "Aloe vera cubes",        cat: "Toppings",  unit: "kg",  stock: 2.4,  par: 6,   daily: 0.6,supplier: "Pearl Bros.",   last: "Mon · 2d", glyph: "卢" },
  { id: "i12", name: "Lychee jelly",           cat: "Toppings",  unit: "kg",  stock: 1.8,  par: 6,   daily: 0.7,supplier: "Pearl Bros.",   last: "Mon · 2d", glyph: "果" },
  { id: "i13", name: "Cream cheese foam mix",  cat: "Dairy",     unit: "kg",  stock: 3.0,  par: 8,   daily: 0.9,supplier: "Daily Dairy",   last: "Today",    glyph: "酪" },
  { id: "i14", name: "Oat milk",               cat: "Dairy",     unit: "L",   stock: 8.4,  par: 14,  daily: 2.4,supplier: "Oatley",        last: "Wed · 5d", glyph: "燕" },
  { id: "i15", name: "Honey",                  cat: "Syrups",    unit: "L",   stock: 2.6,  par: 4,   daily: 0.3,supplier: "Bee Local",     last: "Last wk",  glyph: "蜜" },
  { id: "i16", name: "Mochi (assorted)",       cat: "Snacks",    unit: "ct",  stock: 64,   par: 120, daily: 18, supplier: "Wagashi Atelier",last: "Tue · 4d",glyph: "餅" },
  { id: "i17", name: "Pineapple cakes",        cat: "Snacks",    unit: "ct",  stock: 96,   par: 150, daily: 14, supplier: "Wagashi Atelier",last: "Tue · 4d",glyph: "鳳" },
];
const INVENTORY_CATS = ["All", "Tea", "Toppings", "Syrups", "Dairy", "Fruit", "Snacks"];

window.MENU_ITEMS = MENU_ITEMS;
window.CATEGORIES = CATEGORIES;
window.ORDERS = ORDERS;
window.FEED = FEED;
window.SIZE_OPTIONS = SIZE_OPTIONS;
window.SWEETNESS_LEVELS = SWEETNESS_LEVELS;
window.ICE_LEVELS = ICE_LEVELS;
window.ADDONS = ADDONS;
window.ORDER_SOURCES = ORDER_SOURCES;
window.INVENTORY = INVENTORY;
window.INVENTORY_CATS = INVENTORY_CATS;
