/* Patients — the directory, one person's record, and registering a new one.
   Commit model: the list and the record are INSTANT (every control applies on
   its own, consequential ones raise an undo toast). /patients/new is its own
   registered route and is STAGED: nothing is written until Save. */
(() => {
  const {
    Button, Badge, Avatar, Checkbox, Select, Input, Textarea, Field, FieldLabel, FieldDescription, FieldError,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
  } = window.OriginDesignSystem_7c998b;

  const STATUS_FILTER = [{ value: "all", label: "All care states" }].concat(PATIENT_STATUS.map((s) => ({ value: s.id, label: s.label })));
  const WARD_FILTER = [{ value: "all", label: "All wards" }].concat(WARDS.map((w) => ({ value: w.value, label: wardName(w.value) })));

  function PatientsScreen({ data, now, viewer, onNavigate, onNewPatient, onImport, onExport }) {
    const [query, setQuery] = React.useState("");
    const [status, setStatus] = React.useState("all");
    const [ward, setWard] = React.useState("all");
    const [selected, setSelected] = React.useState([]);
    const { sort, onSort } = useSort("name", "asc");

    const admissionOf = (patient) => admissionOfPatient(data.admissions, patient.id);
    const dirty = !!query || status !== "all" || ward !== "all";
    const clear = () => { setQuery(""); setStatus("all"); setWard("all"); };

    const filtered = data.patients.filter((patient) => {
      const admission = admissionOf(patient);
      if (status !== "all" && patient.status !== status) return false;
      if (ward !== "all" && (!admission || admission.ward !== ward)) return false;
      if (query) {
        const q = query.toLowerCase();
        if ((patient.name + " " + patient.number + " " + patient.gp).toLowerCase().indexOf(q) < 0) return false;
      }
      return true;
    });
    const rows = sortRows(filtered, sort, {
      name: (p) => p.name,
      born: (p) => Date.parse(p.born),
      review: (p) => p.lastReviewAt,
      orders: (p) => activeOrders(ordersForPatient(data.orders, p.id)).length,
    });
    const chosen = data.patients.filter((p) => selected.indexOf(p.id) > -1);

    return (
      <>
        <PageHeader
          path="/patients"
          title="Patients"
          sub={"Everybody registered at " + HD_HOSPITAL.short + ", whether they are on a ward today or not."}
          actions={
            <>
              <ImportButton viewer={viewer} onImport={onImport} />
              <ExportMenu scope={dirty ? filtered.length + " patients matching the filters" : "All " + data.patients.length + " patients"}
                onExport={(format) => onExport("Patient directory", dirty ? "Filtered patient list" : "All patients", filtered.length, format)} />
              <Button size="sm" onClick={onNewPatient}><Icon name="plus" />New patient</Button>
            </>
          }
        />
        <Band flush>
          <FilterBar query={query} onQuery={setQuery} placeholder="Search name, hospital number or GP…" dirty={dirty} onClear={clear}
            selects={[
              { id: "status", label: "Care state", items: STATUS_FILTER, value: status, onChange: setStatus },
              { id: "ward", label: "Ward", items: WARD_FILTER, value: ward, onChange: setWard },
            ]} />
          <BulkBar count={chosen.length} note="Export writes one row per patient with their current ward and care state." onClear={() => setSelected([])}>
            <ExportMenu scope={chosen.length + " selected patients"} onExport={(format) => { onExport("Patient selection", chosen.map((p) => p.name).join(", "), chosen.length, format); setSelected([]); }} />
          </BulkBar>
          {rows.length === 0 ? <NoResults noun="patients" onClear={clear} /> : (
            <>
              <TableWrap>
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead style={{ width: 36 }}>
                        <Checkbox aria-label="Select every patient in this list"
                          checked={rows.length > 0 && rows.every((p) => selected.indexOf(p.id) > -1)}
                          indeterminate={selected.length > 0 && !rows.every((p) => selected.indexOf(p.id) > -1)}
                          onCheckedChange={(on) => setSelected(on ? rows.map((p) => p.id) : [])} />
                      </TableHead>
                      <TableHead><SortHead label="Patient" sortKey="name" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead><SortHead label="Born" sortKey="born" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead>Care state</TableHead>
                      <TableHead>Where</TableHead>
                      <TableHead>Alerts</TableHead>
                      <TableHead className="hd-num"><SortHead label="Active orders" sortKey="orders" sort={sort} onSort={onSort} /></TableHead>
                      <TableHead><SortHead label="Last reviewed" sortKey="review" sort={sort} onSort={onSort} /></TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {rows.map((patient) => {
                      const admission = admissionOf(patient);
                      const orders = activeOrders(ordersForPatient(data.orders, patient.id));
                      return (
                        <TableRow key={patient.id}>
                          <TableCell>
                            <Checkbox aria-label={"Select " + patient.name} checked={selected.indexOf(patient.id) > -1}
                              onCheckedChange={(on) => setSelected((list) => (on ? list.concat([patient.id]) : list.filter((id) => id !== patient.id)))} />
                          </TableCell>
                          <TableCell>
                            <PersonCell name={patient.name} avatar={patient.avatar} meta={patient.number + " · " + patient.sex}
                              onOpen={() => onNavigate("/patients/" + patient.id)} />
                          </TableCell>
                          <TableCell>{patient.born}</TableCell>
                          <TableCell><StatusBadge values={PATIENT_STATUS} value={patient.status} /></TableCell>
                          <TableCell>
                            {admission && (admission.status === "on_ward" || admission.status === "fit_for_discharge")
                              ? wardName(admission.ward) + " " + admission.bed
                              : admission && admission.status === "awaiting_bed"
                                ? <span className="hd-warn">Waiting for a bed on {wardName(admission.ward)}</span>
                                : <span className="hd-muted">Not on a ward</span>}
                          </TableCell>
                          <TableCell>{PATIENT_FLAGS.hasAllergyAlert(patient) ? <AllergyChips patient={patient} /> : <span className="hd-muted">None recorded</span>}</TableCell>
                          <TableCell className="hd-num">{orders.length}</TableCell>
                          <TableCell><Clock at={patient.lastReviewAt} now={now} problem={PATIENT_FLAGS.reviewOverdue(patient, now)} /></TableCell>
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </TableWrap>
              <TableFoot shown={rows.length} total={data.patients.length} noun="patients" />
            </>
          )}
        </Band>
      </>
    );
  }

  function PatientScreen({ data, now, viewer, patientId, onNavigate, onAdmissionTransition, onMedicationTransition, onPostNote, onReactToNote, onExport }) {
    const patient = patientById(data.patients, patientId);
    const admission = admissionOfPatient(data.admissions, patient.id);
    const orders = ordersForPatient(data.orders, patient.id);
    const live = activeOrders(orders).concat(ordersAwaitingPharmacy(orders)).concat(ordersOnHold(orders));
    const procedures = interventionsForPatient(data.interventions, patient.id);
    const booked = procedures.filter((i) => i.status === "requested" || i.status === "scheduled");
    const notes = commentsFor(data.comments, "patient", patient.id);
    const patientInvoices = invoicesForPatient(data.invoices, patient.id);
    const onWardNow = admission && (admission.status === "on_ward" || admission.status === "fit_for_discharge");

    return (
      <>
        <PageHeader path="/patients/:id" params={{ id: patient.id }} crumb={patient.name} title={patient.name}
          actions={<ExportMenu scope={"This record: admission, chart and " + procedures.length + " procedures"} onExport={(format) => onExport("Patient record", patient.name + " · " + patient.number, 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">{patient.name}</span>
              <span className="hd-record__meta">
                <span>{patient.number}</span>
                <span>Born {patient.born}</span>
                <span>{patient.sex}</span>
                <StatusBadge values={PATIENT_STATUS} value={patient.status} />
                <AllergyChips patient={patient} />
              </span>
            </div>
          </div>
          {admission ? (
            <div className="hd-record__actions">
              <Transitions transitions={admissionTransitions(admission, viewer)} onRun={(t) => onAdmissionTransition(admission, t)} />
            </div>
          ) : null}
        </div>

        <Figures three items={[
          { label: onWardNow ? "Day of stay" : "Days since last review", value: onWardNow ? ADMISSION_FLAGS.lengthOfStayDays(admission, now) : Math.round((now - patient.lastReviewAt) / DAY_MS), qualifier: onWardNow ? "of an expected " + admission.expectedStayDays + " on " + wardName(admission.ward) : "reviewed " + formatWhen(patient.lastReviewAt, now) },
          { label: "Orders on the chart", value: live.length, qualifier: ordersAwaitingPharmacy(orders).length + " awaiting a pharmacist, " + ordersOnHold(orders).length + " on hold" },
          { label: "Procedures booked", value: booked.length, qualifier: booked.filter((i) => INTERVENTION_FLAGS.consentMissing(i)).length + " without consent recorded" },
        ]} />

        <Band title="Admission" note={admission ? labelOf(ADMISSION_SOURCES, admission.source) + " · " + admission.reason : null}>
          {admission ? (
            <Pairs items={[
              { label: "Status", value: <StatusBadge values={ADMISSION_STATUS} value={admission.status} /> },
              { label: "Ward and bed", value: admission.bed ? wardName(admission.ward) + " " + admission.bed : wardName(admission.ward) + " — no bed yet" },
              { label: "Consultant", value: staffName(data.staff, admission.consultantId) },
              { label: "Bed requested", value: <Clock at={admission.requestedAt} now={now} problem={ADMISSION_FLAGS.bedWaitBreached(admission, now)} /> },
              { label: "Admitted", value: admission.admittedAt ? formatWhen(admission.admittedAt, now) : "Not yet on a ward" },
              { label: "Expected stay", value: admission.expectedStayDays + " days" + (ADMISSION_FLAGS.overExpectedStay(admission, now) ? " — over by " + (ADMISSION_FLAGS.lengthOfStayDays(admission, now) - admission.expectedStayDays) : "") },
              { label: "Fit for discharge", value: admission.fitAt ? formatWhen(admission.fitAt, now) : "Not yet" },
              { label: "Left the ward", value: admission.dischargedAt ? formatWhen(admission.dischargedAt, now) : "Still here" },
            ]} />
          ) : <p className="hd-footnote">No ward episode on this record — {patient.name} is seen as {statusValue(PATIENT_STATUS, patient.status).label.toLowerCase()}.</p>}
        </Band>

        <Band flush title={"Medication chart (" + live.length + ")"} note="Orders that can still be given: active, awaiting verification or on hold.">
          {live.length ? (
            <TableWrap>
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Drug</TableHead>
                    <TableHead>Dose and route</TableHead>
                    <TableHead>Status</TableHead>
                    <TableHead>Next dose</TableHead>
                    <TableHead>Prescriber</TableHead>
                    <TableHead>Action</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {live.map((order) => (
                    <TableRow key={order.id}>
                      <TableCell>
                        <span className="hd-person__text">
                          <span className="hd-person__name">{order.drug}</span>
                          <span className="hd-person__meta">{order.indication}</span>
                        </span>
                      </TableCell>
                      <TableCell>{order.dose} · {labelOf(DRUG_ROUTES, order.route)} · {order.frequency.toLowerCase()}</TableCell>
                      <TableCell><StatusBadge values={MEDICATION_STATUS} value={order.status} /></TableCell>
                      <TableCell><Clock at={order.nextDoseAt} now={now} problem dangerous={MEDICATION_FLAGS.doseMissed(order, now)} /></TableCell>
                      <TableCell>{staffName(data.staff, order.prescriberId)}</TableCell>
                      <TableCell><RowActions transitions={medicationTransitions(order, viewer)} onRun={(t) => onMedicationTransition(order, t)} label={"Actions for " + order.drug} /></TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </TableWrap>
          ) : <p className="hd-footnote" style={{ paddingInline: "var(--gutter-page)" }}>Nothing on the chart. Every course this patient has been given has finished or been stopped.</p>}
        </Band>

        <Band title={"Procedures (" + procedures.length + ")"} note="Everything requested for this patient, most recent first.">
          {procedures.length ? (
            <div className="hd-rows">
              {procedures.map((intervention) => (
                <div className="hd-row" key={intervention.id}>
                  <div className="hd-row__text">
                    <span className="hd-row__title">
                      <button type="button" className="hd-linkbtn" onClick={() => onNavigate("/interventions/" + intervention.id)}>{intervention.name}</button>
                    </span>
                    <span className="hd-row__note">
                      {intervention.scheduledAt ? formatWhen(intervention.scheduledAt, now) + " · " + labelOf(THEATRES, intervention.theatre) : "Not scheduled — requested " + formatWhen(intervention.requestedAt, now)} · {staffName(data.staff, intervention.surgeonId)}
                    </span>
                  </div>
                  <StatusBadge values={INTERVENTION_STATUS} value={intervention.status} />
                  {INTERVENTION_FLAGS.consentMissing(intervention) ? <Badge size="sm" variant="warning">Consent not recorded</Badge> : null}
                </div>
              ))}
            </div>
          ) : <p className="hd-footnote">Nothing has been requested for this patient.</p>}
        </Band>

        <Band title={"Billing (" + patientInvoices.length + ")"} note="Invoices raised for this patient's care. A total is the sum of the invoice's lines; a balance is what its cleared payments have not covered.">
          {patientInvoices.length ? (
            <div className="hd-rows">
              {patientInvoices.map((invoice) => {
                const balance = invoiceBalance(invoice, data.payments);
                return (
                  <div className="hd-row" key={invoice.id}>
                    <div className="hd-row__text">
                      <span className="hd-row__title">
                        <button type="button" className="hd-linkbtn" onClick={() => onNavigate("/invoices/" + invoice.id)}>{invoice.reference}</button>
                      </span>
                      <span className="hd-row__note">
                        {labelOf(PAYERS, invoice.payer)} · {formatPounds(invoiceTotal(invoice))} over {invoice.lines.length} {invoice.lines.length === 1 ? "line" : "lines"}
                        {balance ? " · " + formatPounds(balance) + " outstanding" : " · settled"}
                        {invoice.dueAt ? " · due " + formatWhen(invoice.dueAt, now) : " · not issued"}
                      </span>
                    </div>
                    <StatusBadge values={INVOICE_STATUS} value={invoice.status} />
                    {INVOICE_FLAGS.overdue(invoice, balance, now) ? <Badge size="sm" variant="warning">{formatCountdown(invoice.dueAt, now)}</Badge> : null}
                  </div>
                );
              })}
            </div>
          ) : <p className="hd-footnote">Nothing has been billed for this patient.</p>}
        </Band>

        <Band title={"Notes (" + notes.length + ")"} note="Written on this record by anybody with access to it. Posting is immediate.">
          <NoteThread comments={notes} staff={data.staff} viewer={viewer} now={now}
            onReact={onReactToNote} onPost={(body) => onPostNote({ type: "patient", id: patient.id }, body)}
            placeholder={"Something the next shift needs to know about " + patient.name.split(" ")[0] + "…"} />
        </Band>
      </>
    );
  }

  /* Registering somebody is a real form, so it is a route and it is staged
     (§5). Nothing is written into the patient index until Save. */
  function NewPatientScreen({ data, viewer, onCreate, onNavigate }) {
    const empty = { name: "", born: "", sex: "Female", status: "outpatient", gp: "", phone: "", nextOfKin: "", allergies: "" };
    const [values, setValues] = React.useState(empty);
    const [submitted, setSubmitted] = React.useState(false);
    const set = (id, value) => setValues((v) => Object.assign({}, v, { [id]: value }));
    const dirty = Object.keys(empty).some((key) => values[key] !== empty[key]);

    const errors = {};
    if (!values.name.trim() || values.name.trim().split(/\s+/).length < 2) errors.name = "Enter a full name — family name and given name.";
    else if (data.patients.some((p) => p.name.toLowerCase() === values.name.trim().toLowerCase())) errors.name = "Somebody with that name is already registered. Check the directory before you add a second record.";
    if (!/^\d{1,2} [A-Za-z]{3} \d{4}$/.test(values.born.trim())) errors.born = "Use the form 14 Feb 1958.";
    if (!values.gp.trim()) errors.gp = "Name the practice, or write “Out of area”.";
    if (!/^[\d ]{9,}$/.test(values.phone.trim())) errors.phone = "Enter a contact number.";
    const valid = Object.keys(errors).length === 0;

    return (
      <>
        <PageHeader path="/patients/new" title="New patient" sub="Registering somebody adds them to the patient index and gives them a hospital number. It does not admit them to a ward." />
        <Band>
          <div className="hd-fields">
            <Field>
              <FieldLabel htmlFor="hd-p-name">Full name</FieldLabel>
              <Input id="hd-p-name" value={values.name} placeholder="Ruth Adeyemi" invalid={submitted && !!errors.name} onChange={(e) => set("name", e.target.value)} />
              {submitted && errors.name ? <FieldError>{errors.name}</FieldError> : <FieldDescription>As it appears on their identity document.</FieldDescription>}
            </Field>
            <Field>
              <FieldLabel htmlFor="hd-p-born">Date of birth</FieldLabel>
              <Input id="hd-p-born" value={values.born} placeholder="14 Feb 1958" invalid={submitted && !!errors.born} onChange={(e) => set("born", e.target.value)} />
              {submitted && errors.born ? <FieldError>{errors.born}</FieldError> : <FieldDescription>Day, three-letter month, year.</FieldDescription>}
            </Field>
            <Field>
              <FieldLabel>Sex recorded at registration</FieldLabel>
              <Select aria-label="Sex recorded at registration" value={values.sex} onValueChange={(v) => set("sex", v)}
                items={[{ value: "Female", label: "Female" }, { value: "Male", label: "Male" }, { value: "Not stated", label: "Not stated" }]} />
            </Field>
            <Field>
              <FieldLabel>Care state</FieldLabel>
              <Select aria-label="Care state" value={values.status} onValueChange={(v) => set("status", v)}
                items={PATIENT_STATUS.filter((s) => s.id === "outpatient" || s.id === "day_case").map((s) => ({ value: s.id, label: s.label }))} />
              <FieldDescription>A new record starts outside the wards; admitting them is a separate step on Admissions.</FieldDescription>
            </Field>
            <Field>
              <FieldLabel htmlFor="hd-p-gp">GP practice</FieldLabel>
              <Input id="hd-p-gp" value={values.gp} placeholder="Fern Lane Surgery" invalid={submitted && !!errors.gp} onChange={(e) => set("gp", e.target.value)} />
              {submitted && errors.gp ? <FieldError>{errors.gp}</FieldError> : null}
            </Field>
            <Field>
              <FieldLabel htmlFor="hd-p-phone">Contact number</FieldLabel>
              <Input id="hd-p-phone" value={values.phone} placeholder="07700 900 118" invalid={submitted && !!errors.phone} onChange={(e) => set("phone", e.target.value)} />
              {submitted && errors.phone ? <FieldError>{errors.phone}</FieldError> : null}
            </Field>
            <Field>
              <FieldLabel htmlFor="hd-p-kin">Next of kin</FieldLabel>
              <Input id="hd-p-kin" value={values.nextOfKin} placeholder="Deborah Adeyemi (daughter)" onChange={(e) => set("nextOfKin", e.target.value)} />
              <FieldDescription>Leave it blank and the record reads “Not recorded”.</FieldDescription>
            </Field>
            <Field className="hd-fields__wide">
              <FieldLabel htmlFor="hd-p-allergies">Known allergies</FieldLabel>
              <Textarea id="hd-p-allergies" rows={2} value={values.allergies} placeholder="Penicillin, latex" onChange={(e) => set("allergies", e.target.value)} />
              <FieldDescription>Separate them with commas. Each one becomes an alert on every clinical screen that shows this patient.</FieldDescription>
            </Field>
          </div>
        </Band>
        <FormBar dirty={dirty} label="Save patient"
          note={"A hospital number is issued on save, in the " + HD_HOSPITAL.short + " series."}
          onCancel={() => { setValues(empty); setSubmitted(false); onNavigate("/patients"); }}
          onSave={() => { setSubmitted(true); if (valid) onCreate(values); }} />
      </>
    );
  }

  Object.assign(window, { PatientsScreen, PatientScreen, NewPatientScreen });
})();
