// Cashier shift management — opening float count + close-out reconciliation.

function ShiftOpenSheet({ onConfirm }) {
  const [v, setV] = React.useState("");
  return (
    <div className="sheet-wrap" onMouseDown={() => {}}>
      <div className="sheet" onMouseDown={(e) => e.stopPropagation()}>
        <header className="sheet-head">
          <div>
            <div className="eyebrow mono">START OF SHIFT</div>
            <h3>Count the cash drawer</h3>
          </div>
        </header>
        <div className="sheet-body">
          <p className="muted">
            Enter the cash amount in the drawer right now. This is your opening
            float — we'll compare it against your closing count at end of shift.
          </p>
          <label className="field" style={{ marginTop: 14 }}>
            <span className="field-label mono">Opening cash (Rp)</span>
            <input type="number" inputMode="numeric" autoFocus value={v}
              onChange={(e) => setV(e.target.value.replace(/\D/g, ""))}
              placeholder="500000" />
          </label>
          <div className="muted mono" style={{ fontSize: 12, marginTop: 6 }}>
            Tip: count carefully. You'll be asked to count again at close.
          </div>
        </div>
        <footer className="sheet-foot">
          <button className="btn-primary" disabled={!v}
            onClick={() => onConfirm({ openingCash: Number(v) || 0 })}>
            Start shift
          </button>
        </footer>
      </div>
    </div>
  );
}

function ShiftCloseSheet({ shift, orders, onClose, onConfirm }) {
  const { db } = usePOS();
  const [counted, setCounted] = React.useState("");
  const [refundsByMethod, setRefundsByMethod] = React.useState({});
  const [refundedItems, setRefundedItems] = React.useState([]);
  const [cashExpenses, setCashExpenses] = React.useState(0);
  const [autoPrint, setAutoPrint] = React.useState(true);
  const cashier = shift.cashierUid;
  // Boundary for "this shift" — a cashier can open/close multiple shifts in
  // one calendar day, so date alone isn't enough to scope a shift's own
  // sales/refunds/expenses; everything below is also filtered to
  // createdAt >= openedAt, or a second shift the same day would double-count
  // whatever the previous shift already reconciled. A doc whose serverTimestamp
  // hasn't resolved locally yet (createdAt still null) is treated as "now" —
  // it can only be a write from the current, already-open shift.
  const openedAtMs = shift.openedAt?.toDate ? shift.openedAt.toDate().getTime() : 0;
  const sinceOpen = (createdAt) => {
    const ms = createdAt?.toDate ? createdAt.toDate().getTime() : Date.now();
    return ms >= openedAtMs;
  };

  // Pull today's refunds (grouped by method) and cash expenses. Query by the
  // single indexed cashierUid field and filter dateKey client-side — this
  // avoids any composite-index requirement (a multi-equality query can fail
  // silently) and stays live via onSnapshot so the reconciliation updates if
  // a refund is issued while the close sheet is open. We track refunds for
  // every method (cash/qris/card): cash refunds reduce the drawer, while
  // qris/card refunds reduce their respective sales lines and the net total.
  React.useEffect(() => {
    const today = dayKey();
    const unsubR = db.collection("refunds")
      .where("cashierUid", "==", cashier)
      .onSnapshot((s) => {
        const byMethod = {};
        const items = [];
        s.docs.map((d) => d.data())
          .filter((r) => r.dateKey === today && sinceOpen(r.createdAt))
          .forEach((r) => {
            const m = (r.method || "cash").toLowerCase();
            byMethod[m] = (byMethod[m] || 0) + (r.total || 0);
            (r.items || []).forEach((it) => items.push(it));
          });
        setRefundsByMethod(byMethod);
        setRefundedItems(items);
      }, (e) => console.warn("Shift refunds query failed:", e));
    const unsubE = db.collection("expenses")
      .where("cashierUid", "==", cashier)
      .onSnapshot((s) => {
        setCashExpenses(s.docs
          .map((d) => d.data())
          .filter((x) => x.dateKey === today && sinceOpen(x.createdAt) && (x.method || "cash").toLowerCase() === "cash")
          .reduce((a, x) => a + (x.amount || 0), 0));
      }, (e) => console.warn("Shift expenses query failed:", e));
    return () => { unsubR(); unsubE(); };
  }, [cashier]);

  const cashRefunds = refundsByMethod.cash || 0;
  const qrisRefunds = refundsByMethod.qris || 0;
  const cardRefunds = refundsByMethod.card || 0;
  const totalRefunds = Object.values(refundsByMethod).reduce((a, v) => a + v, 0);

  // Scoped to this shift's own window, not just "today" — see sinceOpen above.
  const paidOrdersThisShift = orders.filter((o) => o.paid && o.cashierUid === cashier && sinceOpen(o.createdAt));

  // Split totals by payment method across this shift's paid orders.
  const totals = paidOrdersThisShift.reduce((acc, o) => {
    const splits = o.payments && o.payments.length ? o.payments : [{ method: o.paymentMethod, amount: o.total }];
    splits.forEach((p) => {
      const m = (p.method || "other").toLowerCase();
      acc[m] = (acc[m] || 0) + (Number(p.amount) || 0);
    });
    return acc;
  }, {});
  // Sales lines shown net of refunds for the same method.
  const cashSales = totals.cash || 0;
  const qrisSales = (totals.qris || 0) - qrisRefunds;
  const cardSales = (totals.card || 0) - cardRefunds;
  const grossSales = paidOrdersThisShift.reduce((s, o) => s + (o.total || 0), 0);
  const totalSales = grossSales - totalRefunds;

  // Drawer only moves with physical cash: opening + cash sales − cash refunds − cash expenses.
  const expected = (shift.openingCash || 0) + cashSales - cashRefunds - cashExpenses;
  const variance = (Number(counted) || 0) - expected;
  const ordersToday = paidOrdersThisShift.length;

  // Item-level breakdown for the shift summary printout: qty sold per
  // product name this shift, net of refunded qty. See computeItemsSold below.
  const itemsSold = React.useMemo(
    () => computeItemsSold(paidOrdersThisShift, refundedItems),
    [paidOrdersThisShift, refundedItems]
  );

  const handleClose = async () => {
    const closedAt = new Date();
    await onConfirm({
      closingCash: Number(counted) || 0,
      expectedCash: expected, cashSales, refunds: cashRefunds,
    });
    if (autoPrint && window.PRINTER && window.PRINTER.shift) {
      const openedAt = shift.openedAt?.toDate ? shift.openedAt.toDate() : null;
      const fmtTime = (d) => d ? d.toLocaleTimeString("id-ID", { hour: "2-digit", minute: "2-digit" }) : "—";
      PRINTER.shift({
        cashierEmail: shift.cashierEmail || "",
        dateKey: dayKey(),
        openedAt: fmtTime(openedAt),
        closedAt: fmtTime(closedAt),
        openingCash: shift.openingCash || 0,
        totalTransactions: ordersToday,
        totalSales,
        itemsSold,
        cashSales, qrisSales, cardSales,
        cashRefunds,
        qrisRefunds,
        cardRefunds,
        totalRefunds,
        cashExpenses,
        expectedCash: expected,
        countedCash: Number(counted) || 0,
        variance,
      });
    }
  };

  return (
    <div className="sheet-wrap" onMouseDown={onClose}>
      <div className="sheet sheet-wide" onMouseDown={(e) => e.stopPropagation()}>
        <header className="sheet-head">
          <div>
            <div className="eyebrow mono">END OF SHIFT</div>
            <h3>Cash drawer reconciliation</h3>
          </div>
          <button className="icon-x" onClick={onClose}>×</button>
        </header>
        <div className="sheet-body">
          <div className="panel-card" style={{ background: "var(--paper)", border: "1px solid var(--line)" }}>
            <div className="reco">
              <div className="reco-row"><span>Net sales · {ordersToday} orders</span><Mono>{money(totalSales)}</Mono></div>
              {totalRefunds > 0 && <div className="reco-row"><span className="muted">Refunds (all methods)</span><Mono className="muted">−{money(totalRefunds)}</Mono></div>}
              <div className="reco-row"><span>Opening float</span><Mono>{money(shift.openingCash || 0)}</Mono></div>
              <div className="reco-row"><span>Cash sales</span><Mono>+{money(cashSales)}</Mono></div>
              {qrisSales !== 0 && <div className="reco-row"><span>QRIS sales{qrisRefunds > 0 ? " (net)" : ""}</span><Mono>{money(qrisSales)}</Mono></div>}
              {cardSales !== 0 && <div className="reco-row"><span>Card sales{cardRefunds > 0 ? " (net)" : ""}</span><Mono>{money(cardSales)}</Mono></div>}
              {cashRefunds > 0 && <div className="reco-row"><span>Cash refunds</span><Mono>−{money(cashRefunds)}</Mono></div>}
              {qrisRefunds > 0 && <div className="reco-row"><span className="muted">QRIS refunds (no drawer impact)</span><Mono className="muted">−{money(qrisRefunds)}</Mono></div>}
              {cardRefunds > 0 && <div className="reco-row"><span className="muted">Card refunds (no drawer impact)</span><Mono className="muted">−{money(cardRefunds)}</Mono></div>}
              {cashExpenses > 0 && <div className="reco-row"><span>Cash expenses</span><Mono>−{money(cashExpenses)}</Mono></div>}
              <div className="reco-row reco-total"><span>Expected in drawer</span><Mono>{money(expected)}</Mono></div>
            </div>
          </div>

          <label className="field" style={{ marginTop: 18 }}>
            <span className="field-label mono">Counted now (Rp)</span>
            <input type="number" inputMode="numeric" autoFocus value={counted}
              onChange={(e) => setCounted(e.target.value.replace(/\D/g, ""))}
              placeholder={String(expected)} />
          </label>

          {counted !== "" && (
            <div className={"variance-pill " + (variance === 0 ? "ok" : variance > 0 ? "over" : "short")}>
              <span>Variance</span>
              <Mono>{variance >= 0 ? "+" : "−"}{money(Math.abs(variance))}</Mono>
              <span className="muted mono" style={{ fontSize: 12 }}>
                {variance === 0 ? "Balanced" : variance > 0 ? "Over (extra cash)" : "Short"}
              </span>
            </div>
          )}

          <label className="row" style={{ gap: 8, marginTop: 14, alignItems: "center" }}>
            <input type="checkbox" checked={autoPrint} onChange={(e) => setAutoPrint(e.target.checked)} />
            <span className="mono">Print shift summary on cashier receipt printer</span>
          </label>
        </div>
        <footer className="sheet-foot">
          <button className="btn-ghost" onClick={onClose}>Cancel</button>
          <button className="btn-primary" disabled={counted === ""} onClick={handleClose}>
            Close shift
          </button>
        </footer>
      </div>
    </div>
  );
}

// Cashier petty cash / expense log — entries that pull cash out of the
// drawer (e.g. buy ice, pay courier). Subtracted from expected cash on
// shift close so the reconciliation balances.
function ExpenseSheet({ cashierUid, cashierEmail, onClose }) {
  const { db, showToast } = usePOS();
  const [amount, setAmount] = React.useState("");
  const [description, setDescription] = React.useState("");
  const [method, setMethod] = React.useState("cash");
  const [busy, setBusy] = React.useState(false);
  const [expenses, setExpenses] = React.useState([]);

  React.useEffect(() => {
    const today = dayKey();
    return db.collection("expenses")
      .where("cashierUid", "==", cashierUid)
      .where("dateKey", "==", today)
      .onSnapshot((s) => {
        const list = s.docs.map((d) => ({ id: d.id, ...d.data() }));
        list.sort((a, b) => (b.createdAt?.seconds || 0) - (a.createdAt?.seconds || 0));
        setExpenses(list);
      });
  }, [cashierUid]);

  const add = async () => {
    if (!amount || !description.trim()) return;
    setBusy(true);
    try {
      await db.collection("expenses").add({
        cashierUid,
        cashierEmail: cashierEmail || null,
        amount: Number(amount) || 0,
        description: description.trim(),
        method,
        dateKey: dayKey(),
        createdAt: firebase.firestore.FieldValue.serverTimestamp(),
      });
      setAmount(""); setDescription(""); setMethod("cash");
      showToast("Expense recorded");
    } catch (e) { alert("Failed: " + e.message); }
    finally { setBusy(false); }
  };

  const remove = async (id) => {
    if (!confirm("Remove this expense?")) return;
    await db.collection("expenses").doc(id).delete();
  };

  const cashTotal = expenses.filter((e) => e.method === "cash").reduce((s, e) => s + (e.amount || 0), 0);

  return (
    <div className="sheet-wrap" onMouseDown={onClose}>
      <div className="sheet sheet-wide" onMouseDown={(e) => e.stopPropagation()}>
        <header className="sheet-head">
          <div>
            <div className="eyebrow mono">PETTY CASH · TODAY</div>
            <h3>Expenses from the drawer</h3>
            <div className="muted mono" style={{ fontSize: 12 }}>
              Cash out so far: {money(cashTotal)} · these subtract from expected cash on shift close.
            </div>
          </div>
          <button className="icon-x" onClick={onClose}>×</button>
        </header>
        <div className="sheet-body">
          <div className="row" style={{ gap: 8, flexWrap: "wrap", alignItems: "flex-end" }}>
            <label className="field" style={{ flex: "0 0 140px" }}>
              <span className="field-label mono">Amount (Rp)</span>
              <input type="number" inputMode="numeric" value={amount}
                onChange={(e) => setAmount(e.target.value.replace(/\D/g, ""))}
                placeholder="50000" />
            </label>
            <label className="field" style={{ flex: 1, minWidth: 200 }}>
              <span className="field-label mono">Description</span>
              <input value={description} onChange={(e) => setDescription(e.target.value)}
                placeholder="e.g. beli es batu, bayar kurir" />
            </label>
            <label className="field" style={{ flex: "0 0 120px" }}>
              <span className="field-label mono">Paid via</span>
              <select value={method} onChange={(e) => setMethod(e.target.value)}>
                <option value="cash">CASH</option>
                <option value="qris">QRIS</option>
                <option value="card">CARD</option>
              </select>
            </label>
            <button className="btn-primary btn-sm" disabled={!amount || !description.trim() || busy} onClick={add}>
              {busy ? "…" : "Add"}
            </button>
          </div>

          <div style={{ marginTop: 18 }}>
            {expenses.length === 0
              ? <div className="muted mono">No expenses logged yet.</div>
              : (
                <table className="data-table">
                  <thead><tr><th>Time</th><th>Description</th><th>Method</th><th>Amount</th><th></th></tr></thead>
                  <tbody>
                    {expenses.map((e) => (
                      <tr key={e.id}>
                        <td><Mono>{e.createdAt?.toDate ? e.createdAt.toDate().toLocaleTimeString("id-ID", { hour: "2-digit", minute: "2-digit" }) : "—"}</Mono></td>
                        <td>{e.description}</td>
                        <td><Mono>{(e.method || "cash").toUpperCase()}</Mono></td>
                        <td><Mono>{money(e.amount)}</Mono></td>
                        <td><button className="btn-ghost-sm" onClick={() => remove(e.id)}>Remove</button></td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              )}
          </div>
        </div>
      </div>
    </div>
  );
}

// Aggregates qty sold per product name across a set of paid orders, net
// of refunded qty for the same product names (mirrors the "net of
// refunds" convention used for revenue elsewhere). Synthetic refund
// lines (e.g. "QRIS unique code") aren't part of any order's items, so
// they just net to a negative count and get filtered out — no special-
// casing needed. Shared by ShiftCloseSheet (end-of-shift printout) and
// the admin "Print shift summary" button in cashier.jsx (mid-shift
// snapshot, no need to close first).
function computeItemsSold(paidOrders, refundedItems) {
  const counts = {};
  (paidOrders || []).forEach((o) => {
    (o.items || []).forEach((it) => {
      if (!it.name) return;
      counts[it.name] = (counts[it.name] || 0) + (Number(it.qty) || 0);
    });
  });
  (refundedItems || []).forEach((it) => {
    if (!it.name) return;
    counts[it.name] = (counts[it.name] || 0) - (Number(it.qty) || 0);
  });
  return Object.entries(counts)
    .map(([name, qty]) => ({ name, qty }))
    .filter((x) => x.qty > 0)
    .sort((a, b) => b.qty - a.qty || a.name.localeCompare(b.name));
}

Object.assign(window, { ShiftOpenSheet, ShiftCloseSheet, ExpenseSheet, computeItemsSold });
