// store.jsx — global state, talks to /api/* (Cloudflare Pages Functions + D1).
// Mutations apply optimistically, then reconcile with the server response.

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

const StoreContext = createContext(null);

function StoreProvider({ children }) {
  const [items, setItems] = useState([]);
  const [locations, setLocations] = useState([]);
  const [stock, setStock] = useState([]);
  const [movements, setMovements] = useState([]);
  const [ready, setReady] = useState(false);
  const [toasts, setToasts] = useState([]);
  const toastIdRef = useRef(1);

  const pushToast = useCallback((msg, kind = "ok") => {
    const id = toastIdRef.current++;
    setToasts((ts) => [...ts, { id, msg, kind }]);
    setTimeout(() => setToasts((ts) => ts.filter((t) => t.id !== id)), 3200);
  }, []);

  // Initial bootstrap
  useEffect(() => {
    let cancelled = false;
    (async () => {
      const data = await window.api.bootstrap();
      if (cancelled) return;
      setItems(data.items || []);
      setLocations(data.locations || []);
      setStock(data.stock || []);
      setMovements(data.movements || []);
      setReady(true);
      if (data.source === "fallback") {
        pushToast("ใช้ข้อมูลจำลอง — เชื่อมต่อ API ไม่ได้", "err");
      }
    })();
    return () => { cancelled = true; };
  }, [pushToast]);

  // Stock helpers
  const stockMap = useMemo(() => {
    const m = new Map();
    stock.forEach((s) => m.set(s.sku + "@" + s.loc, s.qty));
    return m;
  }, [stock]);

  const getStock = useCallback((sku, loc) => stockMap.get(sku + "@" + loc) || 0, [stockMap]);
  const getStockBySku = useCallback((sku) => stock.filter((s) => s.sku === sku), [stock]);
  const getStockByLoc = useCallback((loc) => stock.filter((s) => s.loc === loc), [stock]);
  const totalQty = useCallback((sku) => stock.filter((s) => s.sku === sku).reduce((a, b) => a + b.qty, 0), [stock]);

  // Merge a list of {sku, loc, qty} updates into local stock state.
  // Zero-qty rows are kept so the UI can still render the location row.
  const mergeStock = useCallback((updates) => {
    setStock((prev) => {
      const map = new Map(prev.map((s) => [s.sku + "@" + s.loc, s]));
      updates.forEach((u) => {
        map.set(u.sku + "@" + u.loc, { sku: u.sku, loc: u.loc, qty: u.qty });
      });
      return [...map.values()];
    });
  }, []);

  // ── Operations ──────────────────────────────────────────────────
  const receive = useCallback(async ({ sku, loc, qty, supplier, note, user }) => {
    const item = items.find((i) => i.sku === sku);
    if (!item) return null;
    try {
      const res = await window.api.receive({ sku, loc, qty, supplier, note, user });
      mergeStock(res.stock);
      setMovements((ms) => [res.movement, ...ms]);
      pushToast(`รับเข้า ${item.name} จำนวน ${qty} ${item.unit} เรียบร้อย`, "ok");
      return res.movement;
    } catch (err) {
      pushToast("บันทึกการรับเข้าไม่สำเร็จ: " + err.message, "err");
      return null;
    }
  }, [items, mergeStock, pushToast]);

  const issue = useCallback(async ({ sku, loc, qty, customer, note, user }) => {
    const item = items.find((i) => i.sku === sku);
    if (!item) return null;
    const have = stockMap.get(sku + "@" + loc) || 0;
    if (have < qty) {
      pushToast(`สต๊อกไม่พอ! มีเพียง ${have} ${item.unit}`, "err");
      return null;
    }
    try {
      const res = await window.api.issue({ sku, loc, qty, customer, note, user });
      mergeStock(res.stock);
      setMovements((ms) => [res.movement, ...ms]);
      pushToast(`จ่ายออก ${item.name} จำนวน ${qty} ${item.unit} เรียบร้อย`, "ok");
      return res.movement;
    } catch (err) {
      pushToast("บันทึกการจ่ายออกไม่สำเร็จ: " + err.message, "err");
      return null;
    }
  }, [items, stockMap, mergeStock, pushToast]);

  const transfer = useCallback(async ({ sku, fromLoc, toLoc, qty, note, user }) => {
    const item = items.find((i) => i.sku === sku);
    if (!item) return null;
    const have = stockMap.get(sku + "@" + fromLoc) || 0;
    if (have < qty) {
      pushToast(`สต๊อกต้นทางไม่พอ! มีเพียง ${have}`, "err");
      return null;
    }
    try {
      const res = await window.api.transfer({ sku, fromLoc, toLoc, qty, note, user });
      mergeStock(res.stock);
      setMovements((ms) => [res.movement, ...ms]);
      pushToast(`โอน ${item.name} จำนวน ${qty} จาก ${fromLoc} → ${toLoc}`, "ok");
      return res.movement;
    } catch (err) {
      pushToast("บันทึกการโอนไม่สำเร็จ: " + err.message, "err");
      return null;
    }
  }, [items, stockMap, mergeStock, pushToast]);

  const value = {
    items, setItems,
    locations, setLocations,
    stock, movements,
    ready,
    getStock, getStockBySku, getStockByLoc, totalQty,
    receive, issue, transfer,
    toasts, pushToast,
  };

  return <StoreContext.Provider value={value}>{children}</StoreContext.Provider>;
}

function useStore() {
  const ctx = useContext(StoreContext);
  if (!ctx) throw new Error("StoreProvider missing");
  return ctx;
}

// Helpers
function fmtNum(n) {
  if (n === null || n === undefined || isNaN(n)) return "—";
  return n.toLocaleString("en-US");
}
function fmtMoney(n) {
  return "฿" + (n || 0).toLocaleString("en-US", { minimumFractionDigits: 0 });
}
function fmtDate(ts) {
  const d = new Date(ts);
  return d.toLocaleDateString("th-TH", { day: "2-digit", month: "short", year: "2-digit" });
}
function fmtDateTime(ts) {
  const d = new Date(ts);
  const date = d.toLocaleDateString("th-TH", { day: "2-digit", month: "short" });
  const time = d.toLocaleTimeString("th-TH", { hour: "2-digit", minute: "2-digit", hour12: false });
  return `${date} ${time}`;
}
function timeAgo(ts) {
  const diff = Date.now() - ts;
  const m = Math.floor(diff / 60000);
  if (m < 1) return "เมื่อสักครู่";
  if (m < 60) return `${m} นาทีที่แล้ว`;
  const h = Math.floor(m / 60);
  if (h < 24) return `${h} ชม.ที่แล้ว`;
  const d = Math.floor(h / 24);
  return `${d} วันที่แล้ว`;
}

Object.assign(window, {
  StoreProvider, useStore, StoreContext,
  fmtNum, fmtMoney, fmtDate, fmtDateTime, timeAgo,
});
