/* Plan and billing — what Meridian charges this workspace, and what the next
   bill will be. The one settings screen that is a statement rather than a form.
   Commit model: none. There is not a single control on this screen, so there is
   nothing to save and nothing to undo — like /balances, its only actions are
   navigation. A plan change is not offered here rather than offered dead.
   Data state: populated.
   Every figure is computed from the payment, payout, dispute, card and member
   arrays over one named window, at the rates in FEE_SCHEDULE. Nothing about
   money on this screen is stored: a fee typed into a fixture is a fee nobody
   can check against the rows on /payments. */
(() => {
  const {
    Card, CardHeader, CardTitle, CardDescription, CardPanel, CardFooter,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableFooter,
    Button,
  } = window.OriginDesignSystem_7c998b;

  /* The window the next bill is computed over, named once and stated wherever a
     figure on this screen is. */
  const BILLING_WINDOW_DAYS = 30;

  /* The formula lives in metrics.js, so the fee quoted on /payments/new and the
     fee billed here are the same function. */
  const methodFee = (payment) => window.methodFee(payment.method, payment.amount);
  const feeBasis = (method) => {
    const rate = FEE_SCHEDULE.method[method];
    const parts = [];
    if (rate.rate) parts.push((rate.rate * 100).toFixed(1).replace(/\.0$/, "") + "%");
    if (rate.fixedCents) parts.push(formatMoney(rate.fixedCents) + " each");
    if (rate.capCents) parts.push("capped at " + formatMoney(rate.capCents));
    return parts.join(" + ").replace(" + capped", ", capped");
  };

  function BillingScreen({ data, now, workspace, onNavigate }) {
    const plan = BILLING_PLANS.find((p) => p.value === workspace.plan) || BILLING_PLANS[0];
    const nextChargeAt = new Date(new Date(now).getFullYear(), new Date(now).getMonth() + 1, 1).getTime();

    /* One window, one array, and every line below filters it. */
    const window = paymentsInPeriod(data.payments, BILLING_WINDOW_DAYS, now);
    const charged = succeededPayments(window);
    const volume = sumBy(charged, "amount");
    const methods = PAYMENT_METHODS
      .map((method) => {
        const rows = charged.filter((p) => p.method === method.value);
        return { method: method, rows: rows, volume: sumBy(rows, "amount"), fees: rows.reduce((total, p) => total + methodFee(p), 0) };
      })
      .filter((row) => row.rows.length > 0)
      .sort((a, b) => b.fees - a.fees);
    const processingFees = methods.reduce((total, row) => total + row.fees, 0);

    const payoutsPaid = data.payouts.filter((p) => p.status === "paid");
    const payoutFees = payoutsPaid.length * FEE_SCHEDULE.payoutFixedCents;
    const disputesOpened = disputesInPeriod(data.disputes, BILLING_WINDOW_DAYS, now);
    const disputeFees = disputesOpened.length * FEE_SCHEDULE.disputeFixedCents;

    const cards = liveCards(data.cards);
    const extraCards = Math.max(0, cards.length - plan.cardsIncluded);
    const cardFees = extraCards * FEE_SCHEDULE.extraCardCents;
    const seats = data.members.length;
    const extraSeats = Math.max(0, seats - plan.seatsIncluded);
    const seatFees = extraSeats * FEE_SCHEDULE.extraSeatCents;

    const lines = [
      { key: "plan", label: plan.label + " plan", basis: "One month, billed ahead", amount: plan.monthlyCents, route: null },
      { key: "processing", label: "Processing", basis: charged.length + " charges taking " + formatMoney(volume) + " across " + methods.length + " " + (methods.length === 1 ? "method" : "methods"), amount: processingFees, route: "/payments" },
      { key: "payouts", label: "Payouts", basis: payoutsPaid.length + (payoutsPaid.length === 1 ? " transfer that landed, at " : " transfers that landed, at ") + formatMoney(FEE_SCHEDULE.payoutFixedCents) + " each", amount: payoutFees, route: "/payouts" },
      { key: "disputes", label: "Disputes", basis: disputesOpened.length + " opened in the window, at " + formatMoney(FEE_SCHEDULE.disputeFixedCents) + " each", amount: disputeFees, route: "/disputes" },
      { key: "seats", label: "Extra seats", basis: extraSeats ? extraSeats + " over the " + plan.seatsIncluded + " included, at " + formatMoney(FEE_SCHEDULE.extraSeatCents) + " each" : seats + " of " + plan.seatsIncluded + " seats used", amount: seatFees, route: "/settings/team" },
      { key: "cards", label: "Extra cards", basis: extraCards ? extraCards + " over the " + plan.cardsIncluded + " included, at " + formatMoney(FEE_SCHEDULE.extraCardCents) + " each" : cards.length + " of " + plan.cardsIncluded + " live cards", amount: cardFees, route: "/cards" },
    ];
    const total = lines.reduce((sum, line) => sum + line.amount, 0);
    const effectiveRate = volume ? processingFees / volume : null;

    return (
      <div className="fd-page">
        <PageHeader path="/settings/billing" title="Plan and billing" />

        <div className="fd-metrics">
          <MetricTile icon="file-text" label="Next bill" value={formatMoney(total)}
            qualifier={"Charged on " + formatDayFull(nextChargeAt) + ", computed from the last " + BILLING_WINDOW_DAYS + " days"} />
          <MetricTile icon="package" label="Plan" value={plan.label}
            qualifier={formatMoney(plan.monthlyCents) + " a month · " + plan.seatsIncluded + " seats and " + plan.cardsIncluded + " cards included"} />
          <MetricTile icon="trending-up" label="Volume charged on" value={formatMoney(volume)}
            qualifier={charged.length + " succeeded payments in the window; refunded and failed ones carry no fee"} />
          <MetricTile icon="percent" label="Effective rate" value={effectiveRate === null ? "No volume" : formatPercent(effectiveRate)}
            qualifier={formatMoney(processingFees) + " of processing over the " + formatMoney(volume) + " it was charged on"} />
        </div>

        <Card>
          <CardHeader>
            <CardTitle>The next bill</CardTitle>
            <CardDescription>
              Every line is the rate in the contract over the rows on the destination it names, for the {BILLING_WINDOW_DAYS} days to today. Nothing here is a stored figure.
            </CardDescription>
          </CardHeader>
          <div className="fd-card-table">
            <Table variant="card">
              <TableHeader>
                <TableRow>
                  <TableHead>Line</TableHead>
                  <TableHead>What it is charged on</TableHead>
                  <TableHead className="fd-num">Amount</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {lines.map((line) => (
                  <TableRow key={line.key}>
                    <TableCell>
                      <span className="fd-record">
                        {line.route
                          ? <button type="button" className="fd-link fd-record__name" onClick={() => onNavigate(line.route)}>{line.label}</button>
                          : <span className="fd-record__name">{line.label}</span>}
                      </span>
                    </TableCell>
                    <TableCell><span className="fd-record__meta">{line.basis}</span></TableCell>
                    <TableCell className="fd-num fd-amount">{line.amount ? formatMoney(line.amount) : <span className="fd-muted">Nothing</span>}</TableCell>
                  </TableRow>
                ))}
              </TableBody>
              <TableFooter>
                <TableRow>
                  <TableCell>Due {formatDay(nextChargeAt)}</TableCell>
                  <TableCell><span className="fd-record__meta">Taken from {data.balance.bank}</span></TableCell>
                  <TableCell className="fd-num fd-amount">{formatMoney(total)}</TableCell>
                </TableRow>
              </TableFooter>
            </Table>
          </div>
          <CardFooter className="fd-card-footer">
            <span className="fd-footnote">A line at nothing is still a line: it says the rate exists and that nothing triggered it this month.</span>
            <Button variant="outline" size="sm" onClick={() => onNavigate("/balances")}>Open balances</Button>
          </CardFooter>
        </Card>

        <div className="fd-columns fd-columns--even">
          <div className="fd-col">
            <Card>
              <CardHeader>
                <CardTitle>Processing, by method</CardTitle>
                <CardDescription>The {formatMoney(processingFees)} above, decomposed by how the money arrived. Share is of the fee, not of the volume — a wire is cheap on a big number and dear on a small one.</CardDescription>
              </CardHeader>
              <CardPanel>
                <div className="fd-stack">
                  {methods.map((row) => (
                    <div className="fd-part" key={row.method.value}>
                      <div className="fd-spread">
                        <span>{row.method.label}<span className="fd-record__meta"> · {feeBasis(row.method.value)}</span></span>
                        <span className="fd-amount">{formatMoney(row.fees)}</span>
                      </div>
                      <span className="fd-bar" role="img" aria-label={row.method.label + ", " + formatMoney(row.fees) + " of fees"}>
                        <span className="fd-bar__fill" style={{ width: (processingFees ? (row.fees / processingFees) * 100 : 0).toFixed(1) + "%" }} />
                      </span>
                      <span className="fd-footnote">
                        {row.rows.length} {row.rows.length === 1 ? "charge" : "charges"} taking {formatMoney(row.volume)} · {formatPercent(row.fees / row.volume)} of what came in that way
                      </span>
                    </div>
                  ))}
                </div>
              </CardPanel>
              <CardFooter className="fd-card-footer">
                <span className="fd-footnote">Sums to {formatMoney(processingFees)}, the processing line above.</span>
                <Button variant="outline" size="sm" onClick={() => onNavigate("/payments")}>Open payments</Button>
              </CardFooter>
            </Card>
          </div>

          <div className="fd-col">
            <Card>
              <CardHeader>
                <CardTitle>What {plan.label} includes</CardTitle>
                <CardDescription>{plan.note}</CardDescription>
              </CardHeader>
              <CardPanel>
                <div className="fd-stack">
                  <div className="fd-part">
                    <div className="fd-spread">
                      <span>Seats</span>
                      <span className="fd-amount">{seats} of {plan.seatsIncluded}{extraSeats ? " + " + extraSeats : ""}</span>
                    </div>
                    <span className="fd-bar" role="img" aria-label={seats + " seats of " + plan.seatsIncluded + " included"}>
                      <span className="fd-bar__fill" style={{ width: Math.min(100, (seats / plan.seatsIncluded) * 100).toFixed(1) + "%" }} />
                    </span>
                    <span className="fd-footnote">
                      {extraSeats
                        ? extraSeats + " beyond the plan, at " + formatMoney(FEE_SCHEDULE.extraSeatCents) + " each a month. Removing somebody on the team screen removes the charge."
                        : plan.seatsIncluded - seats + " still included. The next one after that is " + formatMoney(FEE_SCHEDULE.extraSeatCents) + " a month."}
                    </span>
                  </div>
                  <div className="fd-part">
                    <div className="fd-spread">
                      <span>Live cards</span>
                      <span className="fd-amount">{cards.length} of {plan.cardsIncluded}{extraCards ? " + " + extraCards : ""}</span>
                    </div>
                    <span className="fd-bar" role="img" aria-label={cards.length + " live cards of " + plan.cardsIncluded + " included"}>
                      <span className="fd-bar__fill" style={{ width: Math.min(100, (cards.length / plan.cardsIncluded) * 100).toFixed(1) + "%" }} />
                    </span>
                    <span className="fd-footnote">
                      Cancelled cards keep their record and stop counting. {extraCards ? extraCards + " over, at " + formatMoney(FEE_SCHEDULE.extraCardCents) + " each." : plan.cardsIncluded - cards.length + " still included."}
                    </span>
                  </div>
                  <div className="fd-part">
                    <div className="fd-spread">
                      <span>Payout schedule</span>
                      <span className="fd-amount">{labelOf(PAYOUT_SCHEDULES, workspace.schedule)}</span>
                    </div>
                    <span className="fd-footnote">
                      {PAYOUT_SCHEDULES.find((s) => s.value === workspace.schedule).note} Each transfer that lands costs {formatMoney(FEE_SCHEDULE.payoutFixedCents)}, whatever the schedule.
                    </span>
                  </div>
                </div>
              </CardPanel>
              <CardFooter className="fd-card-footer">
                <span className="fd-footnote">Seats and cards are counted from the team and the card list, not from the plan.</span>
                <Button variant="outline" size="sm" onClick={() => onNavigate("/settings/team")}>Open the team</Button>
              </CardFooter>
            </Card>
          </div>
        </div>
      </div>
    );
  }

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