/* Expenses — the money going out on the workspace's own cards.
   Commit model: INSTANT. Approving applies immediately and raises an undo
   toast, querying opens a staged dialog (it needs a sentence), denying is
   irreversible and opens a confirm dialog.
   Data state: one branch — populated, or the no-results branch when the filters
   match nothing.
   Scope and filters (§5.3): the period select above the figures moves the
   figures and the chart; the text field, the three dropdowns and the selection
   narrow the list only.
   Recession (§5.4): an expense waiting on the person who spent it — queried, or
   still owed a receipt — is recessed, and the row names who is being waited on. */
(() => {
  const {
    Card, CardHeader, CardTitle, CardDescription, CardPanel, CardFooter,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
    Select, Checkbox, Button, Badge,
    Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent,
  } = window.OriginDesignSystem_7c998b;

  function ExpensesScreen({ data, now, period, onPeriodChange, onExpenseTransition, onBulkApprove, pushToast }) {
    const [query, setQuery] = React.useState("");
    const [card, setCard] = React.useState("all");
    const [status, setStatus] = React.useState("all");
    const [category, setCategory] = React.useState("all");
    const [selected, setSelected] = React.useState([]);
    const policy = data.policy;
    const days = Number(period);
    const periodLabel = PERIODS.find((p) => p.value === period).label;
    const nameOf = (id) => (data.members.find((m) => m.id === id) || { name: "Someone who has left" }).name;
    const cardOf = (id) => data.cards.find((c) => c.id === id);

    /* Scope first: every figure and the chart read this array. */
    const scoped = expensesInPeriod(data.expenses, days, now);
    const settled = settledExpenses(scoped);
    const pending = pendingExpenses(scoped);
    const reviewable = reviewableExpenses(scoped, policy);
    const missing = expensesMissingReceipt(expensesAwaitingReview(scoped), policy);
    const queried = queriedExpenses(scoped);
    const buckets = bucketBy(settled, (e) => e.occurredAt, now, days).map((b) => ({ at: b.at, spend: sumBy(b.rows, "amount"), rows: b.rows }));

    /* Then the list filters, which move nothing above them. */
    const filtered = scoped.filter((e) => {
      if (query && (e.id + " " + e.merchant + " " + nameOf(e.spenderId)).toLowerCase().indexOf(query.toLowerCase()) === -1) return false;
      if (card !== "all" && e.cardId !== card) return false;
      if (status !== "all" && e.status !== status) return false;
      if (category !== "all" && e.category !== category) return false;
      return true;
    }).sort((a, b) => b.occurredAt - a.occurredAt);
    const dirty = query !== "" || card !== "all" || status !== "all" || category !== "all";
    const clear = () => { setQuery(""); setCard("all"); setStatus("all"); setCategory("all"); };
    const toggle = (id) => setSelected((l) => (l.indexOf(id) > -1 ? l.filter((x) => x !== id) : l.concat([id])));
    const allPicked = filtered.length > 0 && filtered.every((e) => selected.indexOf(e.id) > -1);
    const picked = scoped.filter((e) => selected.indexOf(e.id) > -1);
    const pickedApprovable = reviewableExpenses(picked, policy);

    return (
      <div className="fd-page">
        <PageHeader
          path="/expenses"
          title="Expenses"
          actions={<ExportMenu scope={periodLabel + " · " + scoped.length + " expenses"} onExport={(format) => pushToast({ title: "Export started", description: scoped.length + " expenses · " + periodLabel.toLowerCase() + " · " + format })} />}
        />

        <div className="fd-scope">
          <div className="fd-scope__control">
            <label className="fd-scope__label" htmlFor="fd-exp-period">Period</label>
            <Select id="fd-exp-period" size="sm" items={PERIODS} value={period} onValueChange={onPeriodChange} />
          </div>
          <p className="fd-scope__note">Figures and the chart cover spend that occurred in the {periodLabel.toLowerCase()}, across every card in {MERIDIAN_WORKSPACE.name}.</p>
        </div>

        <div className="fd-metrics">
          <MetricTile
            icon="receipt" label="Card spend"
            value={formatMoney(cardSpendTotal(scoped))}
            qualifier={settled.length + " settled expenses" + (pending.length ? ", plus " + formatMoney(sumBy(pending, "amount")) + " authorised and not yet settled" : "")}
          />
          <MetricTile
            icon="check-check" label="You can approve now"
            value={formatCount(reviewable.length)}
            qualifier={formatMoney(sumBy(reviewable, "amount")) + " across them" + (missing.length ? "; " + missing.length + " more are waiting on a receipt" : "")}
          />
          <MetricTile
            icon="paperclip" label="Waiting on a receipt"
            value={formatCount(missing.length)}
            qualifier={missing.length
              ? formatMoney(sumBy(missing, "amount")) + " held up — receipts are required over " + formatMoney(policy.receiptThresholdCents) + " and on " + policy.receiptCategories.map((c) => labelOf(EXPENSE_CATEGORIES, c).toLowerCase()).join(" and ")
              : "Every expense waiting on review has its receipt"}
          />
          <MetricTile
            icon="message-square" label="Queried"
            value={formatCount(queried.length)}
            qualifier={queried.length ? formatMoney(sumBy(queried, "amount")) + " sent back to the people who spent it" : "Nothing has been sent back"}
          />
        </div>

        <Card className="fd-card">
          <CardHeader>
            <CardTitle>Spend by day</CardTitle>
            <CardDescription>{formatMoney(cardSpendTotal(scoped))} of settled card spend, {periodLabel.toLowerCase()}. Authorisations that have not settled are outside every bar.</CardDescription>
          </CardHeader>
          <CardPanel>
            <DayChart
              buckets={buckets}
              series={[{ key: "spend", label: "Card spend", rows: [{ key: "rows", noun: "expense" }] }]}
              format={formatMoney}
              label="Settled card spend a day"
              note={"Tallest day: " + formatMoney(Math.max.apply(null, buckets.map((b) => b.spend).concat([0]))) + "."}
            />
          </CardPanel>
        </Card>

        <FilterBar
          query={query} onQuery={setQuery} placeholder="Search by merchant, expense or person" dirty={dirty} onClear={clear}
          selects={[
            { id: "card", label: "Card", value: card, onChange: setCard, items: [{ value: "all", label: "Every card" }].concat(data.cards.map((c) => ({ value: c.id, label: c.label + " ·••• " + c.last4 }))) },
            { id: "status", label: "Status", value: status, onChange: setStatus, items: [{ value: "all", label: "Every status" }].concat(EXPENSE_STATUS.map((s) => ({ value: s.id, label: s.label }))) },
            { id: "category", label: "Category", value: category, onChange: setCategory, items: [{ value: "all", label: "Every category" }].concat(EXPENSE_CATEGORIES.map((c) => ({ value: c.value, label: c.label }))) },
          ]}
        />

        <BulkBar
          count={picked.length}
          note={formatMoney(sumBy(picked, "amount")) + " selected · " + pickedApprovable.length + " of them can be approved now."}
          onClear={() => setSelected([])}
        >
          <Button size="sm" disabled={!pickedApprovable.length} onClick={() => { onBulkApprove(pickedApprovable); setSelected([]); }}>
            Approve {pickedApprovable.length ? pickedApprovable.length : ""} expenses
          </Button>
        </BulkBar>

        <Card>
          <CardHeader>
            <CardTitle>Every expense in the period</CardTitle>
            <CardDescription>Newest first. A row waiting on the person who spent it is recessed and says so instead of carrying a button.</CardDescription>
          </CardHeader>
          {filtered.length === 0 ? (
            <CardPanel>
              <Empty>
                <EmptyHeader>
                  <EmptyMedia variant="icon"><Icon name="search-x" size={18} /></EmptyMedia>
                  <EmptyTitle>No expenses match those filters</EmptyTitle>
                  <EmptyDescription>Nothing in the {periodLabel.toLowerCase()} matches. Widen the filters, or lengthen the period above.</EmptyDescription>
                </EmptyHeader>
                <EmptyContent><Button onClick={clear}>Clear filters</Button></EmptyContent>
              </Empty>
            </CardPanel>
          ) : (
            <div className="fd-card-table">
              <Table variant="card">
                <TableHeader>
                  <TableRow>
                    <TableHead style={{ width: "2.5rem" }}>
                      <Checkbox checked={allPicked} indeterminate={!allPicked && filtered.some((e) => selected.indexOf(e.id) > -1)}
                        aria-label="Select every expense shown" onCheckedChange={(checked) => setSelected(checked ? filtered.map((e) => e.id) : [])} />
                    </TableHead>
                    <TableHead>Expense</TableHead>
                    <TableHead className="fd-drop-lg">Card</TableHead>
                    <TableHead className="fd-drop-md">Spent by</TableHead>
                    <TableHead>Status</TableHead>
                    <TableHead className="fd-num">Amount</TableHead>
                    <TableHead className="fd-num fd-drop-lg">Occurred</TableHead>
                    <TableHead className="fd-actions-head">Action</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {filtered.map((expense) => {
                    const owed = EXPENSE_FLAGS.needsReceipt(expense, policy);
                    const waiting = EXPENSE_FLAGS.waitsOnSpender(expense, policy);
                    const on = cardOf(expense.cardId);
                    return (
                      <TableRow key={expense.id} className={waiting ? "fd-row--recessed" : ""}>
                        <TableCell><Checkbox checked={selected.indexOf(expense.id) > -1} aria-label={"Select " + expense.id} onCheckedChange={() => toggle(expense.id)} /></TableCell>
                        <TableCell>
                          <span className="fd-record">
                            <span className="fd-record__name">{expense.merchant}</span>
                            <span className="fd-record__meta"><code>{expense.id}</code> · {labelOf(EXPENSE_CATEGORIES, expense.category)}</span>
                          </span>
                        </TableCell>
                        <TableCell className="fd-drop-lg fd-muted">{on ? on.label : "—"}<span className="fd-sub">•••• {on ? on.last4 : "????"}</span></TableCell>
                        <TableCell className="fd-drop-md fd-muted">{nameOf(expense.spenderId)}</TableCell>
                        <TableCell>
                          <span className="fd-status">
                            <StatusBadge values={EXPENSE_STATUS} value={expense.status} />
                            {owed ? <Badge size="sm" variant="warning">No receipt</Badge> : null}
                          </span>
                          {waiting ? (
                            <span className="fd-sub">
                              Waiting on {nameOf(expense.spenderId)} — {expense.status === "queried" ? expense.queryNote : "receipt required for " + labelOf(EXPENSE_CATEGORIES, expense.category).toLowerCase() + " spend"}
                            </span>
                          ) : expense.status === "approved" ? (
                            <span className="fd-sub">Signed off by {expense.reviewedBy}</span>
                          ) : expense.status === "denied" ? (
                            <span className="fd-sub">{expense.denyReason}</span>
                          ) : null}
                        </TableCell>
                        <TableCell className="fd-num fd-amount">{formatMoney(expense.amount)}</TableCell>
                        <TableCell className="fd-num fd-muted fd-drop-lg" title={formatAbsolute(expense.occurredAt)}>{formatWhen(expense.occurredAt, now)}</TableCell>
                        <TableCell className="fd-actions">
                          <RowActions transitions={expenseTransitions(expense, policy)} onRun={(t) => onExpenseTransition(expense, t)} label={"Actions for " + expense.id} />
                        </TableCell>
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </div>
          )}
          <CardFooter className="fd-card-footer">
            <span className="fd-footnote">Showing {filtered.length} of {scoped.length} expenses in the {periodLabel.toLowerCase()}.</span>
            <span className="fd-footnote">{formatMoney(cardSpendTotal(filtered))} settled across the rows shown.</span>
          </CardFooter>
        </Card>
      </div>
    );
  }

  Object.assign(window, { ExpensesScreen });
})();
