/* Applications — the queue, and the form that makes one.
   Instant commit model on the list (each control confirms itself, moves raise
   an undo toast); /applications/new is Staged, as every form in this product is.
   List architecture, the same on every list screen in the desk: filter bar,
   view tabs, bulk bar, table, pagination. The tabs narrow the list only — there
   are no figures above them to move. */
(() => {
  const {
    Card, CardHeader, CardTitle, CardDescription, CardPanel, CardFooter,
    Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
    Button, Badge, Checkbox, Alert, AlertTitle, AlertDescription, Separator,
    Field, FieldLabel, FieldDescription, FieldError, Input, Textarea, Select, RadioGroup,
    Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext,
  } = window.OriginDesignSystem_7c998b;

  const PAGE = 8;
  const ALL = "all";

  /* One tab, one predicate — and the count on the tab is that predicate's own
     length, over the same array the table renders. */
  const VIEWS = [
    { value: "ours", label: "Waiting on us", predicate: (rows) => rows.filter(APPLICATION_FLAGS.awaitsUs) },
    { value: "theirs", label: "Waiting on others", predicate: (rows) => rows.filter(APPLICATION_FLAGS.awaitsThem) },
    { value: "settled", label: "Settled", predicate: (rows) => rows.filter((a) => !APPLICATION_FLAGS.isLive(a)) },
    { value: ALL, label: "Everything", predicate: (rows) => rows },
  ];

  function ApplicationsScreen({ data, now, session, onNavigate, onApplicationTransition, onBulkAdvance, onBulkDecline, pushToast }) {
    const [view, setView] = React.useState("ours");
    const [query, setQuery] = React.useState("");
    const [requisition, setRequisition] = React.useState(ALL);
    const [owner, setOwner] = React.useState(ALL);
    const [page, setPage] = React.useState(1);
    const [selected, setSelected] = React.useState([]);

    const dirty = !!query || requisition !== ALL || owner !== ALL;
    const clear = () => { setQuery(""); setRequisition(ALL); setOwner(ALL); setPage(1); };

    /* One filter chain, in one place: the text filter, the two selects, then the
       tab's predicate. Every count on this screen reads a stage of this chain. */
    const filtered = data.applications.filter((application) => {
      const req = requisitionById(data.requisitions, application.requisitionId);
      if (requisition !== ALL && application.requisitionId !== requisition) return false;
      if (owner !== ALL && application.ownerId !== owner) return false;
      if (!query) return true;
      const hay = [application.candidate.name, application.candidate.headline, application.candidate.location, application.ref, req.title].join(" ").toLowerCase();
      return hay.indexOf(query.trim().toLowerCase()) > -1;
    });
    const views = VIEWS.map((v) => ({ value: v.value, label: v.label, count: v.predicate(filtered).length }));
    const rows = VIEWS.find((v) => v.value === view).predicate(filtered)
      .sort((a, b) => stageOverBy(b, now) - stageOverBy(a, now) || b.appliedAt - a.appliedAt);
    const pages = Math.max(1, Math.ceil(rows.length / PAGE));
    const current = Math.min(page, pages);
    const shown = rows.slice((current - 1) * PAGE, current * PAGE);
    const from = rows.length ? (current - 1) * PAGE + 1 : 0;
    const to = Math.min(current * PAGE, rows.length);

    const chosen = data.applications.filter((a) => selected.indexOf(a.id) > -1);
    const allChosen = shown.length > 0 && shown.every((a) => selected.indexOf(a.id) > -1);
    const toggle = (id) => setSelected((list) => (list.indexOf(id) > -1 ? list.filter((x) => x !== id) : list.concat([id])));
    /* One act-ability predicate, again: a batch may only advance the ones the
       desk owes a move on, and the note says so rather than the button lying. */
    const advanceable = chosen.filter((a) => {
      const move = transitionsFor(a).find((t) => t.id === "advance");
      return move && !move.disabled;
    });

    return (
      <div className="rd-page">
        <PageHead path="/applications" title="Applications"
          note={"Every application on the desk. " + plural(awaitingUs(data.applications).length, "one", "of them") + " waiting on us right now."}
          actions={
            <>
              <ExportMenu scope={plural(rows.length, "row", "rows") + " as filtered"} onExport={(format) => pushToast({ title: format + " export started", description: plural(rows.length, "application", "applications") + " — the file lands in your downloads." })} />
              <Button size="sm" onClick={() => onNavigate("/applications/new")}><Icon name="plus" size={14} />New application</Button>
            </>
          } />

        <FilterBar query={query} onQuery={(v) => { setQuery(v); setPage(1); }} placeholder="Search a name, a headline or a reference…"
          dirty={dirty} onClear={clear}
          selects={[
            { id: "req", label: "Requisition", value: requisition, onChange: (v) => { setRequisition(v); setPage(1); },
              items: [{ value: ALL, label: "Every requisition" }].concat(data.requisitions.map((r) => ({ value: r.id, label: r.ref + " · " + r.title }))) },
            { id: "owner", label: "Owner", value: owner, onChange: (v) => { setOwner(v); setPage(1); },
              items: [{ value: ALL, label: "Any owner" }].concat(data.staff.filter((p) => p.role === "recruiter").map((p) => ({ value: p.id, label: p.name }))) },
          ]} />

        <Card>
          <div className="rd-card-tabs"><ViewTabs views={views} value={view} onSelect={(v) => { setView(v); setPage(1); }} /></div>
          <CardPanel>
            <BulkBar count={chosen.length} onClear={() => setSelected([])}
              note={advanceable.length === chosen.length
                ? "All " + chosen.length + " can move on."
                : advanceable.length + " of " + chosen.length + " can move on — the rest are waiting on somebody outside the desk."}>
              <Button variant="outline" size="sm" disabled={!advanceable.length} onClick={() => onBulkAdvance(advanceable, () => setSelected([]))}>Move on</Button>
              <Button variant="destructive-outline" size="sm" onClick={() => onBulkDecline(chosen.filter(APPLICATION_FLAGS.isLive), () => setSelected([]))}>Decline</Button>
            </BulkBar>
          </CardPanel>
          {rows.length === 0 ? (
            <NoResults title="No application matches that"
              description={dirty ? "The filters are narrower than the data. Clearing them brings back " + plural(data.applications.length, "application", "applications") + "." : "This view is empty — nothing is sitting here at the moment."}
              onClear={dirty ? clear : null}
              action={<Button onClick={() => setView(ALL)}>Show everything</Button>} />
          ) : (
            <div className="rd-card-table">
              <Table variant="card">
                <TableHeader>
                  <TableRow>
                    <TableHead style={{ width: "2.5rem" }}>
                      <Checkbox checked={allChosen} aria-label={allChosen ? "Clear this page" : "Select this page"}
                        onCheckedChange={() => setSelected(allChosen ? selected.filter((id) => shown.every((a) => a.id !== id)) : selected.concat(shown.filter((a) => selected.indexOf(a.id) === -1).map((a) => a.id)))} />
                    </TableHead>
                    <TableHead>Candidate</TableHead>
                    <TableHead className="rd-drop-md">Requisition</TableHead>
                    <TableHead>Stage</TableHead>
                    <TableHead className="rd-drop-lg">Progress</TableHead>
                    <TableHead>In stage</TableHead>
                    <TableHead className="rd-drop-lg">Source</TableHead>
                    <TableHead className="rd-actions-head">Action</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {shown.map((application) => {
                    const req = requisitionById(data.requisitions, application.requisitionId);
                    const over = stageOverBy(application, now);
                    const settled = !APPLICATION_FLAGS.isLive(application);
                    return (
                      <TableRow key={application.id} className={settled ? "rd-row--recessed" : undefined}>
                        <TableCell>
                          <Checkbox checked={selected.indexOf(application.id) > -1} aria-label={"Select " + application.candidate.name} onCheckedChange={() => toggle(application.id)} />
                        </TableCell>
                        <TableCell>
                          <CandidateCell application={application} onNavigate={onNavigate}
                            meta={application.candidate.headline + (APPLICATION_FLAGS.isUnread(application) ? " · not read yet" : "")} />
                        </TableCell>
                        <TableCell className="rd-drop-md">
                          <button type="button" className="rd-link" onClick={() => onNavigate("/requisitions/" + req.id)}>{req.title}</button>
                          <span className="rd-sub">{req.ref} · {labelOf(SITES, req.site)}</span>
                        </TableCell>
                        <TableCell>
                          <span className="rd-status">
                            <StatusBadge values={APPLICATION_STAGE} value={application.stage} />
                            {APPLICATION_FLAGS.isStalled(application, now) ? <Badge size="sm" variant="warning">Over</Badge> : null}
                          </span>
                        </TableCell>
                        <TableCell className="rd-drop-lg" style={{ minWidth: "9rem" }}>
                          <Stepper application={application} data={data} now={now} mini />
                        </TableCell>
                        <TableCell>
                          <span className={"rd-clock" + (over > 0 ? " rd-clock--danger" : over === 0 && !settled ? " rd-clock--warning" : "")} title={formatAbsolute(application.stageEnteredAt)}>
                            {settled ? formatDayShort(application.decidedAt) : plural(daysInStage(application, now), "day", "days")}
                          </span>
                          {settled ? null : <span className="rd-sub">{stageAllowance(application) ? "of " + stageAllowance(application) : "no service level"}</span>}
                        </TableCell>
                        <TableCell className="rd-drop-lg">{labelOf(SOURCES, application.source)}</TableCell>
                        <TableCell className="rd-actions">
                          <RowActions transitions={transitionsFor(application)} onRun={(t) => onApplicationTransition(application, t)} label={"Actions for " + application.candidate.name}
                            extra={(close) => <MenuItemOpen onClick={() => { close(); onNavigate("/applications/" + application.id); }} />} />
                        </TableCell>
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </div>
          )}
          {rows.length ? (
            <CardFooter className="rd-card-foot">
              <span className="rd-foot">Showing {from}–{to} of {rows.length} {view === ALL ? "applications" : views.find((v) => v.value === view).label.toLowerCase()}.</span>
              {pages > 1 ? (
                <Pagination>
                  <PaginationContent>
                    <PaginationItem><PaginationPrevious href="#" onClick={(e) => { e.preventDefault(); setPage(Math.max(1, current - 1)); }} /></PaginationItem>
                    {Array.from({ length: pages }).map((_, i) => (
                      <PaginationItem key={i}>
                        <PaginationLink href="#" isActive={current === i + 1} onClick={(e) => { e.preventDefault(); setPage(i + 1); }}>{i + 1}</PaginationLink>
                      </PaginationItem>
                    ))}
                    <PaginationItem><PaginationNext href="#" onClick={(e) => { e.preventDefault(); setPage(Math.min(pages, current + 1)); }} /></PaginationItem>
                  </PaginationContent>
                </Pagination>
              ) : null}
            </CardFooter>
          ) : null}
        </Card>
      </div>
    );
  }

  /* The row menu's one non-transition entry. */
  function MenuItemOpen({ onClick }) {
    const { MenuItem } = window.OriginDesignSystem_7c998b;
    return <MenuItem onClick={onClick}>Open the application</MenuItem>;
  }

  /* ── /applications/new — Staged. A real form, so it is its own route.
     Two legitimate ways in, and the screen offers both: key it in here, or send
     the candidate the form and let them fill it on their side. The second
     writes nothing to the desk — an application appears when they submit, not
     when we ask, because a record with no stage does not exist. ─────────────── */
  function NewApplicationScreen({ data, now, session, onNavigate, onCreateApplication, onInviteCandidate }) {
    const open = data.requisitions.filter((r) => r.status === "open");
    const [values, setValues] = React.useState({
      name: "", headline: "", location: "", requisitionId: open[0] ? open[0].id : "",
      source: "referral", rightToWork: "documents_seen", expectation: "", registration: "", note: "",
    });
    const [photo, setPhoto] = React.useState(null);
    const [photoError, setPhotoError] = React.useState(null);
    const [over, setOver] = React.useState(false);
    const [documents, setDocuments] = React.useState([]);
    const [submitted, setSubmitted] = React.useState(false);
    const [dirty, setDirty] = React.useState(false);
    const set = (id, value) => { setDirty(true); setValues((v) => Object.assign({}, v, { [id]: value })); };
    const requisition = requisitionById(data.requisitions, values.requisitionId);
    const photoInput = React.useRef(null);
    const fileInput = React.useRef(null);

    const readPhoto = (file) => {
      if (!file) return;
      if (!/^image\/(png|jpeg|webp)$/.test(file.type)) { setPhotoError("JPG, PNG or WebP only — that file is " + (file.type || "an unknown type") + "."); return; }
      if (file.size > 2 * 1024 * 1024) { setPhotoError("That is " + (file.size / 1048576).toFixed(1) + " MB. Up to 2 MB."); return; }
      setPhotoError(null);
      const reader = new FileReader();
      reader.onload = () => { setPhoto({ name: file.name, src: reader.result }); setDirty(true); };
      reader.readAsDataURL(file);
    };
    const addFiles = (files) => {
      if (!files || !files.length) return;
      const kindOf = (name) => (/cv|resume/i.test(name) ? "cv" : /state|cover|letter/i.test(name) ? "statement" : "case");
      setDocuments((list) => list.concat([].slice.call(files).map((file) => ({ kind: kindOf(file.name), name: file.name, at: now, sizeKb: Math.max(1, Math.round(file.size / 1024)) }))));
      setDirty(true);
    };

    const errors = {};
    if (!values.name.trim() || values.name.trim().indexOf(" ") === -1) errors.name = "Full name, as it appears on their documents.";
    if (!values.headline.trim()) errors.headline = "One line on who they are — it is what the queue shows.";
    if (!values.requisitionId) errors.requisitionId = "Pick the seat this application is for.";
    if (values.expectation) {
      const minor = Math.round(parseFloat(values.expectation.replace(/[^0-9.]/g, "")) * 100);
      if (!minor || isNaN(minor)) errors.expectation = "Whole pounds, or leave it empty.";
      else if (requisition && minor > requisition.bandMinor[1] * 1.25) errors.expectation = "That is well above the band's top of " + money(requisition.bandMinor[1]) + " — check the figure before you key it in.";
    }
    const valid = Object.keys(errors).length === 0;

    return (
      <div className="rd-page">
        <PageHead path="/applications/new" title="New application"
          note="For the ones that arrive by hand — a referral, a conversation at a careers event, somebody the pool turned up. Applications from the careers site and the jobs board land here on their own."
          actions={<Button variant="outline" size="sm" onClick={onInviteCandidate}><Icon name="mail" size={14} />Send the form to the candidate</Button>} />

        <div className="rd-columns">
          <Card>
            <CardHeader>
              <CardTitle>The candidate</CardTitle>
              <CardDescription>Enough to review them properly. Everything else is added as the application moves.</CardDescription>
            </CardHeader>
            <CardPanel>
              <div className="rd-fields">
                <Field>
                  <FieldLabel htmlFor="rd-name">Full name</FieldLabel>
                  <Input id="rd-name" value={values.name} invalid={submitted && !!errors.name} placeholder="Rowan Ellis-Baptiste" onChange={(e) => set("name", e.target.value)} />
                  {submitted && errors.name ? <FieldError>{errors.name}</FieldError> : null}
                </Field>
                <Field>
                  <FieldLabel>Photograph</FieldLabel>
                  <div className={"rd-upload" + (over ? " rd-upload--over" : "")}
                    onDragOver={(e) => { e.preventDefault(); setOver(true); }}
                    onDragLeave={() => setOver(false)}
                    onDrop={(e) => { e.preventDefault(); setOver(false); readPhoto(e.dataTransfer.files && e.dataTransfer.files[0]); }}>
                    <span className="rd-upload__face">
                      {photo
                        ? <img src={photo.src} alt={"Photograph of " + (values.name.trim() || "the candidate")} />
                        : <Icon name="user-round" size={26} />}
                    </span>
                    <div className="rd-upload__body">
                      <span className="rd-upload__note">
                        {photo
                          ? photo.name + " staged. It becomes their face on every screen when you save."
                          : "No photograph yet — their initials stand in wherever a face appears."}
                      </span>
                      <span className="rd-upload__actions">
                        <Button variant="outline" size="sm" onClick={() => photoInput.current.click()}>
                          <Icon name="upload" size={14} />{photo ? "Replace" : "Upload a photo"}
                        </Button>
                        {photo ? <Button variant="ghost" size="sm" onClick={() => { setPhoto(null); setPhotoError(null); setDirty(true); }}>Remove</Button> : null}
                      </span>
                      {photoError
                        ? <FieldError>{photoError}</FieldError>
                        : <span className="rd-foot">Drop an image here, or pick one. JPG, PNG or WebP; square reads best; up to 2 MB.</span>}
                    </div>
                    <input ref={photoInput} className="rd-sr" type="file" accept="image/png,image/jpeg,image/webp" aria-label="Choose a photograph of the candidate" onChange={(e) => { readPhoto(e.target.files && e.target.files[0]); e.target.value = ""; }} />
                  </div>
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-headline">Headline</FieldLabel>
                  <Input id="rd-headline" value={values.headline} invalid={submitted && !!errors.headline} placeholder="Band 5 nurse, district team, three years" onChange={(e) => set("headline", e.target.value)} />
                  {submitted && errors.headline ? <FieldError>{errors.headline}</FieldError> : <FieldDescription>Shown wherever their name appears.</FieldDescription>}
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-location">Where they are</FieldLabel>
                  <Input id="rd-location" value={values.location} placeholder="Community, north" onChange={(e) => set("location", e.target.value)} />
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-registration">Professional registration</FieldLabel>
                  <Input id="rd-registration" value={values.registration} placeholder="NMC 21F4478" onChange={(e) => set("registration", e.target.value)} />
                  <FieldDescription>{requisition && requisition.registration ? "This seat asks for " + requisition.registration + " registration — the checks will not pass without it." : requisition ? "This seat asks for no professional register." : "Leave empty for non-clinical seats."}</FieldDescription>
                </Field>
                <Field>
                  <FieldLabel>Documents</FieldLabel>
                  {documents.length ? (
                    <div className="rd-list">
                      {documents.map((document, i) => (
                        <div className="rd-list__row" key={document.name + i}>
                          <div className="rd-list__main">
                            <span className="rd-record__name">{document.name}</span>
                            <span className="rd-record__meta">{document.kind === "cv" ? "CV" : document.kind === "statement" ? "Supporting statement" : "Other"} · {document.sizeKb} KB</span>
                          </div>
                          <div className="rd-list__side">
                            <Button variant="ghost" size="xs" onClick={() => setDocuments((list) => list.filter((_, x) => x !== i))}>Remove</Button>
                          </div>
                        </div>
                      ))}
                    </div>
                  ) : null}
                  <span className="rd-actions__row" style={{ justifyContent: "flex-start" }}>
                    <Button variant="outline" size="xs" onClick={() => fileInput.current.click()}><Icon name="paperclip" size={12} />Add documents</Button>
                  </span>
                  <input ref={fileInput} className="rd-sr" type="file" multiple accept=".pdf,.doc,.docx,.png,.jpg" aria-label="Add documents to this application" onChange={(e) => { addFiles(e.target.files); e.target.value = ""; }} />
                  <FieldDescription>
                    {documents.length
                      ? plural(documents.length, "file", "files") + " will be attached to the record. A CV is what the screening step reads."
                      : "The CV, and the supporting statement if there is one. Named by what they are, so the record reads properly later."}
                  </FieldDescription>
                </Field>
              </div>
            </CardPanel>
          </Card>

          <Card>
            <CardHeader>
              <CardTitle>The seat, and where they came from</CardTitle>
              <CardDescription>{requisition ? bandLabel(requisition) + " · target start " + formatDay(requisition.targetStartAt) : "Pick a requisition to see its band."}</CardDescription>
            </CardHeader>
            <CardPanel>
              <div className="rd-fields">
                <Field>
                  <FieldLabel htmlFor="rd-req">Requisition</FieldLabel>
                  <Select id="rd-req" value={values.requisitionId} invalid={submitted && !!errors.requisitionId}
                    items={open.map((r) => ({ value: r.id, label: r.ref + " · " + r.title + " · " + labelOf(SITES, r.site) }))}
                    onValueChange={(v) => set("requisitionId", v)} />
                  {submitted && errors.requisitionId ? <FieldError>{errors.requisitionId}</FieldError> : <FieldDescription>Only open requisitions accept applications. {data.requisitions.filter((r) => r.status === "on_hold").length} is on hold.</FieldDescription>}
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-source">Where they came from</FieldLabel>
                  <Select id="rd-source" value={values.source} items={SOURCES.map((s) => ({ value: s.value, label: s.label }))} onValueChange={(v) => set("source", v)} />
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-expectation">Salary expectation</FieldLabel>
                  <Input id="rd-expectation" value={values.expectation} invalid={submitted && !!errors.expectation} placeholder={requisition ? String(Math.round(requisition.bandMinor[0] / 100)) : "34000"} onChange={(e) => set("expectation", e.target.value)} />
                  {submitted && errors.expectation ? <FieldError>{errors.expectation}</FieldError> : <FieldDescription>Optional. Whole pounds. {requisition ? "Band is " + bandLabel(requisition) + "." : ""}</FieldDescription>}
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-rtw">Right to work</FieldLabel>
                  <RadioGroup name="rd-rtw" value={values.rightToWork} onValueChange={(v) => set("rightToWork", v)}
                    items={RIGHT_TO_WORK.map((r) => ({ value: r.id, label: r.label }))} />
                </Field>
                <Field>
                  <FieldLabel htmlFor="rd-note">First note</FieldLabel>
                  <Textarea id="rd-note" rows={3} value={values.note} placeholder="Who referred them, what they said, anything the reviewer should know before they read the CV." onChange={(e) => set("note", e.target.value)} />
                  <FieldDescription>Posted on the thread in your name. Type @ and a colleague's id to pull them in.</FieldDescription>
                </Field>
              </div>
            </CardPanel>
          </Card>
        </div>

        <FormBar dirty={dirty} label="Save application" onCancel={() => onNavigate("/applications")}
          note={dirty
            ? "It enters at applied, unread, owned by you" + (documents.length ? ", with " + plural(documents.length, "document", "documents") : "") + ", and appears in every count on the desk."
            : "Nothing is saved until you fill this in — Applications is unchanged. Or send the candidate the form and let them fill it themselves."}
          onSave={() => { setSubmitted(true); if (valid) onCreateApplication(Object.assign({}, values, { photo: photo ? photo.src : null, documents })); }} />
      </div>
    );
  }

  Object.assign(window, { ApplicationsScreen, NewApplicationScreen });
})();
