// Central state store — products / orders / prebookings / customers live
// in Firestore. Cashier writes, kitchen/waitress/display subscribe in real time.

const POSStore = React.createContext(null);
const db = firebase.firestore();
const auth = firebase.auth();
const sv = firebase.firestore.FieldValue.serverTimestamp;
const inc = firebase.firestore.FieldValue.increment;

function POSProvider({ children }) {
  const [user, setUser]               = React.useState(null);
  const [role, setRole]               = React.useState(null);
  const [authReady, setAuthReady]     = React.useState(false);
  const [products, setProducts]       = React.useState([]);
  const [orders, setOrders]           = React.useState([]);
  const [prebookings, setPrebookings] = React.useState([]);
  const [customers, setCustomers]     = React.useState([]);
  const [currentShift, setCurrentShift] = React.useState(null);
  const [activeRole, setActiveRole]   = React.useState("cashier");
  const [receiptOrder, setReceiptOrder] = React.useState(null);
  const [toast, setToast]             = React.useState(null);

  // Auth gate + role lookup
  React.useEffect(() => {
    return auth.onAuthStateChanged(async (u) => {
      if (!u) { setUser(null); setRole(null); setAuthReady(true); return; }
      try {
        const snap = await db.collection("users").doc(u.uid).get();
        const r = snap.exists ? snap.data().role : null;
        setUser(u); setRole(r);
        if (r === "kitchen") setActiveRole("kitchen");
        else if (r === "waitress") setActiveRole("waitress");
        else if (r === "display") setActiveRole("display");
        else if (r === "admin") setActiveRole("admin");
        else setActiveRole("cashier");
      } catch (e) { setRole(null); }
      setAuthReady(true);
    });
  }, []);

  // Firestore subscriptions (only after auth)
  React.useEffect(() => {
    if (!user) { setProducts([]); setOrders([]); setPrebookings([]); setCustomers([]); setCurrentShift(null); return; }
    const today = new Date().toISOString().slice(0, 10);
    const unsubs = [
      db.collection("products").onSnapshot((s) =>
        setProducts(s.docs.map((d) => ({ id: d.id, ...d.data() })))),
      db.collection("orders").where("dateKey", "==", today).onSnapshot((s) =>
        setOrders(s.docs
          .map((d) => ({ id: d.id, ...d.data() }))
          .sort((a, b) => (b.createdAt?.seconds || 0) - (a.createdAt?.seconds || 0)))),
      db.collection("prebookings").onSnapshot((s) =>
        setPrebookings(s.docs
          .map((d) => ({ id: d.id, ...d.data() }))
          .sort((a, b) => (b.createdAt?.seconds || 0) - (a.createdAt?.seconds || 0)))),
      db.collection("customers").onSnapshot((s) =>
        setCustomers(s.docs.map((d) => ({ id: d.id, ...d.data() })))),
      db.collection("shifts")
        .where("cashierUid", "==", user.uid)
        .where("status", "==", "open")
        .onSnapshot((s) => {
          setCurrentShift(s.empty ? null : { id: s.docs[0].id, ...s.docs[0].data() });
        }),
    ];
    return () => unsubs.forEach((u) => u && u());
  }, [user]);

  const openShift = async ({ openingCash }) => {
    const today = new Date().toISOString().slice(0, 10);
    await db.collection("shifts").add({
      cashierUid: user.uid,
      cashierEmail: user.email,
      openingCash: Number(openingCash) || 0,
      openedAt: sv(),
      status: "open",
      dateKey: today,
    });
    showToast("Shift opened");
  };

  const closeShift = async ({ closingCash, expectedCash, cashSales, refunds }) => {
    if (!currentShift) return;
    await db.collection("shifts").doc(currentShift.id).update({
      closingCash: Number(closingCash) || 0,
      expectedCash: Number(expectedCash) || 0,
      cashSales: Number(cashSales) || 0,
      refunds: Number(refunds) || 0,
      variance: (Number(closingCash) || 0) - (Number(expectedCash) || 0),
      closedAt: sv(),
      status: "closed",
    });
    showToast("Shift closed");
  };

  // Refund: store a refund doc and stamp the order with the refunded amount/items.
  // Negative loyalty adjustment for the customer if applicable.
  const refundOrder = async (order, { items, reason, method }) => {
    const refundTotal = items.reduce((s, i) => s + (i.price + (i.modPrice || 0)) * i.qty, 0);
    const isFull = refundTotal >= (order.total || 0);
    await db.collection("refunds").add({
      orderId: order.id,
      orderShortId: order.shortId || null,
      customer: order.customer || null,
      items,
      total: refundTotal,
      method: method || (order.paymentMethod || "cash"),
      reason: reason || "",
      cashierUid: user?.uid || null,
      cashierEmail: user?.email || null,
      dateKey: new Date().toISOString().slice(0, 10),
      createdAt: sv(),
    });
    await db.collection("orders").doc(order.id).update({
      refundedAmount: (order.refundedAmount || 0) + refundTotal,
      refundedAt: sv(),
      status: isFull ? "refunded" : (order.status || "open"),
    });
    if (order.customer && order.customer !== "Walk-in") {
      const slug = order.customer.toLowerCase().replace(/\s+/g, "_");
      await db.collection("customers").doc(slug).set({
        totalSpent: inc(-refundTotal),
        points: inc(-Math.floor(refundTotal / 10000)),
      }, { merge: true });
    }
    showToast("Refunded " + (refundTotal ? "Rp " + refundTotal.toLocaleString("id-ID") : ""));
  };

  // Actions
  const showToast = (msg) => {
    const t = { msg, ts: Date.now() };
    setToast(t);
    setTimeout(() => setToast((cur) => (cur && cur.ts === t.ts ? null : cur)), 2600);
  };

  const placeOrder = async (order) => {
    const dateKey = new Date().toISOString().slice(0, 10);
    const paid = order.paid !== false; // default true; explicit false for open bills
    const payload = {
      shortId:      order.shortId,
      table:        order.table || "T",
      customer:     order.customer || "Walk-in",
      items:        order.items.map((i) => ({
        id: i.id, sku: i.sku || "", name: i.name, price: i.price, qty: i.qty,
        station: i.station, mods: i.mods || [], modPrice: i.modPrice || 0,
        cogs: i.cogs || 0, notes: i.notes || "",
      })),
      subtotal:     order.subtotal,
      depositApplied: order.depositApplied || 0,
      uniqueCode:   order.uniqueCode || 0,
      total:        order.total,
      tip:          order.tip || 0,
      payments:     order.payments || [],
      paymentMethod: order.paymentMethod || null,
      status:       "open",
      paid,
      barStatus:    order.barStatus,
      kitchenStatus: order.kitchenStatus,
      prebookingId: order.prebookingId || null,
      cashierUid:   user?.uid || null,
      cashierEmail: user?.email || null,
      dateKey,
      createdAt:    sv(),
    };
    const ref = await db.collection("orders").add(payload);
    if (paid) setReceiptOrder({ ...payload, id: ref.id, createdAt: Date.now() });
    showToast(paid ? `Order ${order.shortId} placed` : `Bill ${order.shortId} saved (open)`);

    if (paid && payload.customer && payload.customer !== "Walk-in") {
      const slug = payload.customer.toLowerCase().replace(/\s+/g, "_");
      await db.collection("customers").doc(slug).set({
        name: payload.customer,
        lastVisit: sv(),
        totalSpent: inc(payload.total),
        visits: inc(1),
        points: inc(Math.floor(payload.total / 10000)),
      }, { merge: true });
    }
    return ref.id;
  };

  // Update an existing order (used when paying an open bill or editing items).
  const updateOrder = async (orderId, patch) => {
    const order = orders.find((o) => o.id === orderId);
    await db.collection("orders").doc(orderId).update(patch);
    if (patch.paid && order) {
      setReceiptOrder({ ...order, ...patch, id: orderId });
      if (order.customer && order.customer !== "Walk-in") {
        const slug = order.customer.toLowerCase().replace(/\s+/g, "_");
        await db.collection("customers").doc(slug).set({
          name: order.customer,
          lastVisit: sv(),
          totalSpent: inc(patch.total ?? order.total ?? 0),
          visits: inc(1),
          points: inc(Math.floor((patch.total ?? order.total ?? 0) / 10000)),
        }, { merge: true });
      }
      showToast("Bill paid · receipt ready");
    }
  };

  const advance = async (orderId, station, nextStatus) => {
    const key = station === "bar" ? "barStatus" : "kitchenStatus";
    await db.collection("orders").doc(orderId).update({ [key]: nextStatus });
  };

  const closeOrder = async (orderId) => {
    await db.collection("orders").doc(orderId).update({ status: "closed" });
    showToast("Order served");
  };

  const consumePrebooking = async (pbId) => {
    await db.collection("prebookings").doc(pbId).update({ status: "checked_in" });
  };

  const addPrebooking = async (pb) => {
    const ref = await db.collection("prebookings").add({ ...pb, createdAt: sv() });
    // Fire-and-forget email notification via our own /api/notify-prebook
    // endpoint (the server POSTs to SendGrid). Best-effort — failures are
    // logged but never block the booking.
    try {
      fetch("/api/notify-prebook", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ id: ref.id, ...pb }),
      }).catch(() => {});
    } catch (_) { /* best-effort */ }
    return ref.id;
  };

  const verifyPrebooking = async (pbId) => {
    await db.collection("prebookings").doc(pbId).update({ status: "verified" });
  };

  // Live display mirror — cashier pushes its current cart so customer-display sees it
  const pushLiveCart = async (cart, totals) => {
    await db.collection("displays").doc("main").set({
      items: cart, ...totals, status: cart.length ? "active" : "idle", updatedAt: sv(),
    });
  };

  const value = {
    user, role, authReady,
    products, orders, prebookings, customers,
    activeRole, setActiveRole,
    placeOrder, updateOrder, advance, closeOrder, consumePrebooking, addPrebooking, verifyPrebooking,
    refundOrder,
    currentShift, openShift, closeShift: closeShift,
    pushLiveCart,
    receiptOrder, setReceiptOrder, toast,
    showToast,
    db, auth, sv, inc,
  };
  return <POSStore.Provider value={value}>{children}</POSStore.Provider>;
}

const usePOS = () => React.useContext(POSStore);

Object.assign(window, { POSStore, POSProvider, usePOS });
