/* Finance — invoices, the money against them, and what is owed.
   Commit model: INSTANT on all three screens. Coding, issuing, disputing and
   resolving apply immediately with an undo toast; recording a payment and
   marking a dispute need fields, so they open staged dialogs from App.jsx;
   writing a balance off is irreversible and opens a confirm dialog.

   Nothing here stores a total. An invoice's total is the sum of its lines, what
   it has been paid is the sum of its CLEARED payments, and its balance is the
   difference — metrics.js owns all three, so the list, the record, the report and
   the nav badge cannot disagree. */
(() => {
  const {
    Button, Badge, Avatar, Checkbox, Select, Alert, AlertTitle, AlertDescription,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableFooter,
  } = window.OriginDesignSystem_7c998b;

  const INV_STATUS_FILTER = [{ value: "all", label: "All statuses" }].concat(INVOICE_STATUS.map((s) => ({ value: s.id, label: s.label })));
  const PAYER_FILTER = [{ value: "all", label: "All payers" }].concat(PAYERS);
  const canIssue = (invoice, viewer) => invoiceTransitions(invoice, viewer).some((t) => t.id === "issue" && !t.disabled);

  function InvoicesScreen({ data, now, viewer, onNavigate, onInvoiceTransition, onBulkIssue, onNewInvoice, onExport }) {
    const [query, setQuery] = React.useState("");
    const [status, setStatus] = React.useState("all");
    const [payer, setPayer] = React.useState("all");
    const [selected, setSelected] = React.useState([]);
    const { sort, onSort } = useSort("issued", "desc");

    const overdue = invoicesOverdue(data.invoices, data.payments, now);
    const unbilled = invoicesUnbilled(data.invoices);
    const outstanding = outstandingTotal(data.invoices, data.payments);
    const disputed = invoicesDisputed(data.invoices);

    const dirty = !!query || status !== "all" || payer !== "all";
    const clear = () => { setQuery(""); setStatus("all"); setPayer("all"); };
    const filtered = data.invoices.filter((invoice) => {
      if (status !== "all" && invoice.status !== status) return false;
      if (payer !== "all" && invoice.payer !== payer) return false;
      if (query) {
        const q = query.toLowerCase();
        const patient = patientById(data.patients, invoice.patientId);
        if ((invoice.reference + " " + patient.name + " " + patient.number).toLowerCase().indexOf(q) < 0) return false;
      }
      return true;
    });
    const rows = sortRows(filtered, sort, {
      issued: (i) => i.issuedAt || 0,
      patient: (i) => patientById(data.patients, i.patientId).name,
      total: (i) => invoiceTotal(i),
      balance: (i) => invoiceBalance(i, data.payments),
      due: (i) => i.dueAt || Infinity,
    });
    const chosen = data.invoices.filter((i) => selected.indexOf(i.id) > -1);
    const issuable = chosen.filter((i) => canIssue(i, viewer));

    return (
      <>
        <PageHeader
          path="/invoices"
          title="Invoices"
          sub={"One invoice per episode of care. Every line names the procedure, the bed days or the medication course it bills, so a payer's query can be answered from the care record."}
          actions={
            <>
              <ExportMenu scope={dirty ? filtered.length + " invoices matching the filters" : "All " + data.invoices.length + " invoices"}
                onExport={(format) => onExport("Invoices", dirty ? "Filtered invoices" : "All invoices", filtered.length, format)} />
              <Button size="sm" onClick={onNewInvoice}><Icon name="plus" />New invoice</Button>
            </>
          }
        />
        <Figures items={[
          { label: "Outstanding", value: formatPounds(outstanding), qualifier: formatPounds(overdue.reduce((n, i) => n + invoiceBalance(i, data.payments), 0)) + " of it past the due date" },
          { label: "Past due", value: overdue.length, qualifier: overdue.length ? "oldest " + formatCountdown(overdue.slice().sort((a, b) => a.dueAt - b.dueAt)[0].dueAt, now) : "nothing is past its due date" },
          { label: "Not yet billed", value: unbilled.length, qualifier: unbilled.filter((i) => i.status === "awaiting_coding").length + " waiting on a coder, worth " + formatPounds(unbilled.reduce((n, i) => n + invoiceTotal(i), 0)) },
          { label: "Disputed", value: disputed.length, qualifier: disputed.length ? formatPounds(disputed.reduce((n, i) => n + invoiceBalance(i, data.payments), 0)) + " held behind a payer's query" : "no payer has queried a bill" },
        ]} />

        {disputed.length ? (
          <div className="hd-alertwrap">
            <Alert variant="warning">
              <AlertTitle>{disputed.length === 1 ? "One invoice is disputed" : disputed.length + " invoices are disputed"}</AlertTitle>
              <AlertDescription>{disputed.map((i) => i.reference + " — " + (i.note || "no query recorded")).join(" · ")}</AlertDescription>
            </Alert>
          </div>
        ) : null}

        <Band flush title={"Invoices (" + data.invoices.length + ")"} note="Totals are the sum of each invoice's lines; the balance is what its cleared payments have not covered.">
          <FilterBar query={query} onQuery={setQuery} placeholder="Search reference, patient or hospital number…" dirty={dirty} onClear={clear}
            selects={[
              { id: "status", label: "Status", items: INV_STATUS_FILTER, value: status, onChange: setStatus },
              { id: "payer", label: "Payer", items: PAYER_FILTER, value: payer, onChange: setPayer },
            ]} />
          <BulkBar count={chosen.length}
            note={issuable.length ? issuable.length + " of them are coded and can be issued" : "none of the selected invoices are coded and ready to issue"}
            onClear={() => setSelected([])}>
            {issuable.length ? <Button size="sm" onClick={() => { onBulkIssue(issuable); setSelected([]); }}>Issue {issuable.length}</Button> : null}
            <ExportMenu scope={chosen.length + " selected invoices"} onExport={(format) => { onExport("Invoice selection", chosen.map((i) => i.reference).join(", "), chosen.length, format); setSelected([]); }} />
          </BulkBar>
          {rows.length === 0 ? <NoResults noun="invoices" onClear={clear} /> : (
            <>
              <TableWrap>
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead style={{ width: 36 }}>
                        <Checkbox aria-label="Select every invoice in this list"
                          checked={rows.length > 0 && rows.every((i) => selected.indexOf(i.id) > -1)}
                          indeterminate={selected.length > 0 && !rows.every((i) => selected.indexOf(i.id) > -1)}
                          onCheckedChange={(on) => setSelected(on ? rows.map((i) => i.id) : [])} />
                      </TableHead>
                      <TableHead>Reference</TableHead>
                      <TableHead><SortHead label="Patient" sortKey="patient" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead>Payer</TableHead>
                      <TableHead>Status</TableHead>
                      <TableHead className="hd-num"><SortHead label="Total" sortKey="total" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead className="hd-num"><SortHead label="Balance" sortKey="balance" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead><SortHead label="Due" sortKey="due" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead>Action</TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {rows.map((invoice) => {
                      const patient = patientById(data.patients, invoice.patientId);
                      const balance = invoiceBalance(invoice, data.payments);
                      const late = INVOICE_FLAGS.overdue(invoice, balance, now);
                      return (
                        <TableRow key={invoice.id}>
                          <TableCell>
                            <Checkbox aria-label={"Select " + invoice.reference} checked={selected.indexOf(invoice.id) > -1}
                              onCheckedChange={(on) => setSelected((list) => (on ? list.concat([invoice.id]) : list.filter((id) => id !== invoice.id)))} />
                          </TableCell>
                          <TableCell>
                            <span className="hd-person__text">
                              <span className="hd-person__name"><button type="button" className="hd-linkbtn" onClick={() => onNavigate("/invoices/" + invoice.id)}>{invoice.reference}</button></span>
                              <span className="hd-person__meta">{invoice.lines.length} {invoice.lines.length === 1 ? "line" : "lines"}</span>
                            </span>
                          </TableCell>
                          <TableCell>
                            <PersonCell name={patient.name} avatar={patient.avatar} size={24} meta={patient.number}
                              onOpen={() => onNavigate("/patients/" + patient.id)} />
                          </TableCell>
                          <TableCell>{labelOf(PAYERS, invoice.payer)}</TableCell>
                          <TableCell>
                            <span className="hd-actions__row">
                              <StatusBadge values={INVOICE_STATUS} value={invoice.status} />
                              {late ? <Badge size="sm" variant="warning">{formatCountdown(invoice.dueAt, now)}</Badge> : null}
                            </span>
                          </TableCell>
                          <TableCell className="hd-num">{formatPounds(invoiceTotal(invoice))}</TableCell>
                          <TableCell className="hd-num">{balance ? formatPounds(balance) : <span className="hd-muted">Settled</span>}</TableCell>
                          <TableCell>{invoice.dueAt ? <Clock at={invoice.dueAt} now={now} problem={late} /> : <span className="hd-muted">Not issued</span>}</TableCell>
                          <TableCell><RowActions transitions={invoiceTransitions(invoice, viewer, balance)} onRun={(t) => onInvoiceTransition(invoice, t)} label={"Actions for " + invoice.reference} /></TableCell>
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </TableWrap>
              <TableFoot shown={rows.length} total={data.invoices.length} noun="invoices" />
            </>
          )}
        </Band>
      </>
    );
  }

  function InvoiceScreen({ data, now, viewer, invoiceId, onNavigate, onInvoiceTransition, onPostNote, onReactToNote, onExport }) {
    const invoice = data.invoices.find((i) => i.id === invoiceId);
    const patient = patientById(data.patients, invoice.patientId);
    const total = invoiceTotal(invoice);
    const paid = invoicePaid(data.payments, invoice.id);
    const balance = invoiceBalance(invoice, data.payments);
    const rows = paymentsFor(data.payments, invoice.id);
    const pending = rows.filter((p) => p.status === "pending");
    const late = INVOICE_FLAGS.overdue(invoice, balance, now);
    const notes = commentsFor(data.comments, "invoice", invoice.id);
    const admission = admissionOfPatient(data.admissions, patient.id);

    /* A line's reference is a real record, so the row opens it. */
    const lineRoute = (ref) => (ref ? recordRoute(data, ref) : null);

    return (
      <>
        <PageHeader path="/invoices/:id" params={{ id: invoice.id }} crumb={invoice.reference} title={invoice.reference}
          actions={<ExportMenu scope={"This invoice, its " + invoice.lines.length + " lines and " + rows.length + " payments"} onExport={(format) => onExport("Invoice", invoice.reference + " · " + patient.name, 1, format)} />} />

        <div className="hd-record">
          <div className="hd-record__id">
            <Avatar size={56} src={patient.avatar} alt={"Photograph of " + patient.name} fallback={initialsOf(patient.name)} />
            <div className="hd-record__text">
              <span className="hd-record__name">{formatPoundsExact(total)}</span>
              <span className="hd-record__meta">
                <button type="button" className="hd-linkbtn" onClick={() => onNavigate("/patients/" + patient.id)}>{patient.name}</button>
                <span>{labelOf(PAYERS, invoice.payer)}</span>
                <StatusBadge values={INVOICE_STATUS} value={invoice.status} />
                {late ? <Badge size="sm" variant="warning">{formatCountdown(invoice.dueAt, now)}</Badge> : null}
              </span>
            </div>
          </div>
          <div className="hd-record__actions">
            <Transitions transitions={invoiceTransitions(invoice, viewer, balance)} onRun={(t) => onInvoiceTransition(invoice, t)} />
          </div>
        </div>

        <Figures three items={[
          { label: "Invoiced", value: formatPounds(total), qualifier: invoice.lines.length + (invoice.lines.length === 1 ? " line" : " lines") + " · " + revenueLineNote(invoice) },
          { label: "Cleared", value: formatPounds(paid), qualifier: rows.filter((p) => p.status === "cleared").length + " cleared payments" + (pending.length ? " · " + formatPounds(pending.reduce((n, p) => n + p.amountMinor, 0)) + " advised but not cleared" : "") },
          { label: "Balance", value: formatPounds(balance), qualifier: balance ? (invoice.dueAt ? "due " + formatWhen(invoice.dueAt, now) + " · " + formatCountdown(invoice.dueAt, now) : "not issued, so nothing is due yet") : "nothing outstanding" },
        ]} />

        {invoice.note ? (
          <div className="hd-alertwrap">
            <Alert variant={invoice.status === "disputed" ? "warning" : "info"}>
              <AlertTitle>{invoice.status === "disputed" ? "The payer has queried this invoice" : invoice.status === "draft" ? "Not issued yet" : "Note on this invoice"}</AlertTitle>
              <AlertDescription>{invoice.note}</AlertDescription>
            </Alert>
          </div>
        ) : null}

        <Band flush title={"Lines (" + invoice.lines.length + ")"} note="Each line bills a record in this patient's care, priced against the payer's contract. Open the record to see what it was.">
          <TableWrap>
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>What is billed</TableHead>
                  <TableHead>Kind</TableHead>
                  <TableHead className="hd-num">Quantity</TableHead>
                  <TableHead className="hd-num">Unit</TableHead>
                  <TableHead>Against contract</TableHead>
                  <TableHead className="hd-num">Line total</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {invoice.lines.map((line, index) => {
                  const route = lineRoute(line.ref);
                  return (
                    <TableRow key={line.label + index}>
                      <TableCell>
                        <span className="hd-person__text">
                          <span className="hd-person__name">
                            {route ? <button type="button" className="hd-linkbtn" onClick={() => onNavigate(route)}>{line.label}</button> : line.label}
                          </span>
                          <span className="hd-person__meta">{line.ref ? recordTitle(data, line.ref.type, line.ref.id) : "Not tied to one record"}</span>
                        </span>
                      </TableCell>
                      <TableCell>{labelOf(BILL_LINE_KINDS, line.kind)}</TableCell>
                      <TableCell className="hd-num">{line.qty}</TableCell>
                      <TableCell className="hd-num">{formatPoundsExact(line.unitMinor)}</TableCell>
                      <TableCell>
                        {(function () {
                          const finding = lineOffContract(data, invoice, line);
                          if (!finding) return <span className="hd-footnote">At contract</span>;
                          return finding.kind === "no_price"
                            ? <Badge size="sm" variant="warning">Not in the contract</Badge>
                            : <Badge size="sm" variant="warning">{finding.difference > 0 ? "Over by " : "Under by "}{formatPoundsExact(Math.abs(finding.difference))}</Badge>;
                        })()}
                      </TableCell>
                      <TableCell className="hd-num">{formatPoundsExact(lineTotal(line))}</TableCell>
                    </TableRow>
                  );
                })}
              </TableBody>
              <TableFooter>
                <TableRow>
                  <TableCell colSpan={5}>Total invoiced</TableCell>
                  <TableCell className="hd-num">{formatPoundsExact(total)}</TableCell>
                </TableRow>
              </TableFooter>
            </Table>
          </TableWrap>
        </Band>

        <div className="hd-cols">
          <div>
            <Band flush fill title={"Payments (" + rows.length + ")"} note="Only cleared money counts against the balance.">
              {rows.length ? (
                <TableWrap>
                  <Table>
                    <TableHeader>
                      <TableRow>
                        <TableHead>Received</TableHead>
                        <TableHead>Method</TableHead>
                        <TableHead>Status</TableHead>
                        <TableHead className="hd-num">Amount</TableHead>
                        <TableHead>Reference</TableHead>
                      </TableRow>
                    </TableHeader>
                    <TableBody>
                      {rows.map((payment) => (
                        <TableRow key={payment.id}>
                          <TableCell><Clock at={payment.receivedAt} now={now} /></TableCell>
                          <TableCell>{labelOf(PAYMENT_METHODS, payment.method)}</TableCell>
                          <TableCell><StatusBadge values={PAYMENT_STATUS} value={payment.status} /></TableCell>
                          <TableCell className="hd-num">{formatPoundsExact(payment.amountMinor)}</TableCell>
                          <TableCell>{payment.reference}</TableCell>
                        </TableRow>
                      ))}
                    </TableBody>
                  </Table>
                </TableWrap>
              ) : <p className="hd-footnote" style={{ paddingInline: "var(--card-pad)" }}>No money has been received against {invoice.reference} yet.</p>}
            </Band>
          </div>
          <div>
            <Band tint fill title="The care this bills">
              <Pairs items={[
                { label: "Patient", value: <button type="button" className="hd-linkbtn" onClick={() => onNavigate("/patients/" + patient.id)}>{patient.name}</button> },
                { label: "Hospital number", value: patient.number },
                { label: "Episode", value: admission ? wardName(admission.ward) + " · " + labelOf(ADMISSION_SOURCES, admission.source) : "No ward episode" },
                { label: "Payer", value: labelOf(PAYERS, invoice.payer) },
                { label: "Issued", value: invoice.issuedAt ? formatWhen(invoice.issuedAt, now) : "Not issued" },
                { label: "Due", value: invoice.dueAt ? formatWhen(invoice.dueAt, now) : "Not issued" },
                { label: "Coded by", value: invoice.codedById ? staffName(data.staff, invoice.codedById) : "Not coded yet" },
                { label: "Age", value: invoice.issuedAt ? INVOICE_FLAGS.ageDays(invoice, now) + " days since issue" : "—" },
              ]} />
            </Band>
          </div>
        </div>

        <Band title={"Notes (" + notes.length + ")"} note="Written on this invoice — what a payer asked, and what was answered. Posting is immediate.">
          <NoteThread comments={notes} staff={data.staff} viewer={viewer} now={now}
            onReact={onReactToNote} onPost={(body) => onPostNote({ type: "invoice", id: invoice.id }, body)}
            placeholder={"What the payer asked about " + invoice.reference + ", and what you answered…"} />
        </Band>
      </>
    );
  }

  /* The one sentence an invoice's figure row needs about its own lines. */
  function revenueLineNote(invoice) {
    const kinds = invoice.lines.reduce((list, line) => (list.indexOf(line.kind) > -1 ? list : list.concat([line.kind])), []);
    return kinds.map((kind) => labelOf(BILL_LINE_KINDS, kind).toLowerCase()).join(", ");
  }

  function PaymentsScreen({ data, now, viewer, onNavigate, onNewPayment, onExport }) {
    const [query, setQuery] = React.useState("");
    const [status, setStatus] = React.useState("all");
    const { sort, onSort } = useSort("received", "desc");

    const cleared = data.payments.filter((p) => p.status === "cleared");
    const pending = data.payments.filter((p) => p.status === "pending");
    const failed = data.payments.filter((p) => p.status === "failed");

    const dirty = !!query || status !== "all";
    const clear = () => { setQuery(""); setStatus("all"); };
    const filtered = data.payments.filter((payment) => {
      if (status !== "all" && payment.status !== status) return false;
      if (query) {
        const invoice = data.invoices.find((i) => i.id === payment.invoiceId);
        const patient = invoice ? patientById(data.patients, invoice.patientId) : null;
        if ((payment.reference + " " + (invoice ? invoice.reference : "") + " " + (patient ? patient.name : "")).toLowerCase().indexOf(query.toLowerCase()) < 0) return false;
      }
      return true;
    });
    const rows = sortRows(filtered, sort, {
      received: (p) => p.receivedAt,
      amount: (p) => p.amountMinor,
    });

    return (
      <>
        <PageHeader
          path="/payments"
          title="Payments"
          sub={"Every movement of money against an invoice. Pending money is advised, not received — it counts against nothing until it clears."}
          actions={
            <>
              <ExportMenu scope={dirty ? filtered.length + " payments matching the filters" : "All " + data.payments.length + " payments"}
                onExport={(format) => onExport("Payments", dirty ? "Filtered payments" : "All payments", filtered.length, format)} />
              <Button size="sm" onClick={onNewPayment}><Icon name="plus" />Record payment</Button>
            </>
          }
        />
        <Figures three items={[
          { label: "Cleared", value: formatPounds(cleared.reduce((n, p) => n + p.amountMinor, 0)), qualifier: cleared.length + " payments across " + cleared.reduce((list, p) => (list.indexOf(p.invoiceId) > -1 ? list : list.concat([p.invoiceId])), []).length + " invoices" },
          { label: "Advised, not cleared", value: formatPounds(pending.reduce((n, p) => n + p.amountMinor, 0)), qualifier: pending.length + (pending.length === 1 ? " pending payment" : " pending payments") },
          { label: "Failed", value: formatPounds(failed.reduce((n, p) => n + p.amountMinor, 0)), qualifier: failed.length ? failed.map((p) => p.note).join(" · ") : "no payment has been returned" },
        ]} />

        <Band flush title={"Payments (" + data.payments.length + ")"}>
          <FilterBar query={query} onQuery={setQuery} placeholder="Search payment or invoice reference…" dirty={dirty} onClear={clear}
            selects={[{ id: "status", label: "Status", items: [{ value: "all", label: "All statuses" }].concat(PAYMENT_STATUS.map((s) => ({ value: s.id, label: s.label }))), value: status, onChange: setStatus }]} />
          {rows.length === 0 ? <NoResults noun="payments" onClear={clear} /> : (
            <>
              <TableWrap>
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead><SortHead label="Received" sortKey="received" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead>Invoice</TableHead>
                      <TableHead>Patient</TableHead>
                      <TableHead>Payer and method</TableHead>
                      <TableHead>Status</TableHead>
                      <TableHead className="hd-num"><SortHead label="Amount" sortKey="amount" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead>Reference</TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {rows.map((payment) => {
                      const invoice = data.invoices.find((i) => i.id === payment.invoiceId);
                      const patient = invoice ? patientById(data.patients, invoice.patientId) : null;
                      return (
                        <TableRow key={payment.id}>
                          <TableCell><Clock at={payment.receivedAt} now={now} /></TableCell>
                          <TableCell><button type="button" className="hd-linkbtn" onClick={() => onNavigate("/invoices/" + payment.invoiceId)}>{invoice ? invoice.reference : payment.invoiceId}</button></TableCell>
                          <TableCell>{patient ? <PersonCell name={patient.name} avatar={patient.avatar} size={24} onOpen={() => onNavigate("/patients/" + patient.id)} /> : <span className="hd-muted">—</span>}</TableCell>
                          <TableCell>{labelOf(PAYERS, payment.payer)} · {labelOf(PAYMENT_METHODS, payment.method)}</TableCell>
                          <TableCell><StatusBadge values={PAYMENT_STATUS} value={payment.status} /></TableCell>
                          <TableCell className="hd-num">{formatPoundsExact(payment.amountMinor)}</TableCell>
                          <TableCell>{payment.reference}</TableCell>
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </TableWrap>
              <TableFoot shown={rows.length} total={data.payments.length} noun="payments" />
            </>
          )}
        </Band>
      </>
    );
  }

  function RevenueScreen({ data, now, viewer, onNavigate, onExport }) {
    const payers = revenueByPayer(data.invoices, data.payments);
    const kinds = revenueByKind(data.invoices);
    const aged = agedDebt(data.invoices, data.payments, now);
    const billed = billedTotal(data.invoices);
    const collected = collectedTotal(data.invoices, data.payments);
    const outstanding = outstandingTotal(data.invoices, data.payments);
    const unbilled = invoicesUnbilled(data.invoices);
    const rate = billed ? collected / billed : 0;

    return (
      <>
        <PageHeader
          path="/revenue"
          title="Revenue and debt"
          sub={"Every figure divides the " + invoicesBillable(data.invoices).length + " issued invoices — drafts and invoices awaiting coding are counted separately, because nothing has been asked for yet."}
          actions={<ExportMenu scope={"Revenue by payer, by kind, and the aged debt"} onExport={(format) => onExport("Revenue report", "Issued invoices as at " + new Date(now).toLocaleDateString("en-GB", { day: "numeric", month: "short" }), invoicesBillable(data.invoices).length, format)} />}
        />
        <Figures items={[
          { label: "Billed", value: formatPounds(billed), qualifier: "across " + invoicesBillable(data.invoices).length + " issued invoices" },
          { label: "Collected", value: formatPounds(collected), qualifier: Math.round(rate * 100) + "% of what was billed" },
          { label: "Outstanding", value: formatPounds(outstanding), qualifier: formatPounds(aged.slice(1).reduce((n, b) => n + b.total, 0)) + " of it past due" },
          { label: "Not yet billed", value: formatPounds(unbilled.reduce((n, i) => n + invoiceTotal(i), 0)), qualifier: unbilled.length + " drafts and invoices awaiting coding" },
        ]} />

        <div className="hd-cols hd-cols--wide">
          <div className="hd-stack">
            <Band flush title={"By payer (" + payers.filter((p) => p.invoices).length + ")"} note="Billed, collected and outstanding over the same set of issued invoices.">
              <TableWrap>
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead>Payer</TableHead>
                      <TableHead className="hd-num">Invoices</TableHead>
                      <TableHead className="hd-num">Billed</TableHead>
                      <TableHead style={{ minWidth: "8rem" }}>Share of billing</TableHead>
                      <TableHead className="hd-num">Collected</TableHead>
                      <TableHead className="hd-num">Outstanding</TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {payers.filter((p) => p.invoices).map((row) => (
                      <TableRow key={row.payer}>
                        <TableCell>{row.label}</TableCell>
                        <TableCell className="hd-num">{row.invoices}</TableCell>
                        <TableCell className="hd-num">{formatPounds(row.billed)}</TableCell>
                        <TableCell>
                          <span style={{ display: "flex", flexDirection: "column", gap: 4, minWidth: "7rem" }}>
                            <span className="hd-footnote">{Math.round(row.share * 100)}%</span>
                            <StockBar percent={row.share * 100} />
                          </span>
                        </TableCell>
                        <TableCell className="hd-num">{formatPounds(row.collected)}</TableCell>
                        <TableCell className="hd-num">{row.outstanding ? formatPounds(row.outstanding) : <span className="hd-muted">Settled</span>}</TableCell>
                      </TableRow>
                    ))}
                  </TableBody>
                  <TableFooter>
                    <TableRow>
                      <TableCell>All payers</TableCell>
                      <TableCell className="hd-num">{invoicesBillable(data.invoices).length}</TableCell>
                      <TableCell className="hd-num">{formatPounds(billed)}</TableCell>
                      <TableCell />
                      <TableCell className="hd-num">{formatPounds(collected)}</TableCell>
                      <TableCell className="hd-num">{formatPounds(outstanding)}</TableCell>
                    </TableRow>
                  </TableFooter>
                </Table>
              </TableWrap>
            </Band>
            <Band flush title={"Past due (" + invoicesOverdue(data.invoices, data.payments, now).length + ")"} note="Issued, with a balance, and past the date the payer was given.">
              {invoicesOverdue(data.invoices, data.payments, now).length ? (
                <TableWrap>
                  <Table>
                    <TableHeader>
                      <TableRow>
                        <TableHead>Reference</TableHead>
                        <TableHead>Patient</TableHead>
                        <TableHead>Payer</TableHead>
                        <TableHead className="hd-num">Balance</TableHead>
                        <TableHead>Overdue by</TableHead>
                        <TableHead>Action</TableHead>
                      </TableRow>
                    </TableHeader>
                    <TableBody>
                      {invoicesOverdue(data.invoices, data.payments, now).slice().sort((a, b) => a.dueAt - b.dueAt).map((invoice) => {
                        const patient = patientById(data.patients, invoice.patientId);
                        return (
                          <TableRow key={invoice.id}>
                            <TableCell><button type="button" className="hd-linkbtn" onClick={() => onNavigate("/invoices/" + invoice.id)}>{invoice.reference}</button></TableCell>
                            <TableCell><PersonCell name={patient.name} avatar={patient.avatar} size={24} onOpen={() => onNavigate("/patients/" + patient.id)} /></TableCell>
                            <TableCell>{labelOf(PAYERS, invoice.payer)}</TableCell>
                            <TableCell className="hd-num">{formatPounds(invoiceBalance(invoice, data.payments))}</TableCell>
                            <TableCell><span className="hd-warn">{formatCountdown(invoice.dueAt, now)}</span></TableCell>
                            <TableCell><Button variant="outline" size="sm" onClick={() => onNavigate("/invoices/" + invoice.id)}>Open</Button></TableCell>
                          </TableRow>
                        );
                      })}
                    </TableBody>
                  </Table>
                </TableWrap>
              ) : <p className="hd-footnote" style={{ paddingInline: "var(--card-pad)" }}>Nothing is past due. Every issued invoice with a balance is still inside the time its payer was given.</p>}
            </Band>
          </div>
          <div className="hd-stack">
            <Band title="Aged debt" note="Every issued invoice with a balance, in the bucket its due date puts it in. An invoice appears once.">
              <div className="hd-stack">
                {aged.map((bucket) => (
                  <div className="hd-tile" key={bucket.id}>
                    <span className="hd-tile__label">{bucket.label}</span>
                    <span className="hd-tile__value">{formatPounds(bucket.total)}</span>
                    <StockBar percent={outstanding ? (bucket.total / outstanding) * 100 : 0} tone={bucket.id === "current" ? null : "warn"} />
                    <span className="hd-footnote">
                      {bucket.rows.length
                        ? bucket.rows.map((row) => row.invoice.reference).join(", ")
                        : "nothing in this bucket"}
                    </span>
                  </div>
                ))}
              </div>
            </Band>
            <Band tint title="What was billed for" note="Every line on an issued invoice, grouped by kind.">
              <div className="hd-stack">
                {kinds.map((kind) => (
                  <div className="hd-row hd-row--quiet" key={kind.kind}>
                    <div className="hd-row__text">
                      <span className="hd-row__title">{kind.label}</span>
                      <span className="hd-row__note">{kind.lines} {kind.lines === 1 ? "line" : "lines"} · {Math.round((kind.total / billed) * 100)}% of billing</span>
                    </div>
                    <span className="hd-tile__value">{formatPounds(kind.total)}</span>
                  </div>
                ))}
              </div>
            </Band>
          </div>
        </div>

      </>
    );
  }

  Object.assign(window, { InvoicesScreen, InvoiceScreen, PaymentsScreen, RevenueScreen });
})();
