/* Claims Desk — shared parts, local to this product. Reported in README as
   local additions: TimeBar / ClaimClock (the deadline device), Ledger rows,
   FormDialog (a staged dialog with two or three fields), RecordHead. Nothing
   here holds data or assigns a status tone — StatusBadge does that. */
(() => {
  const {
    Button, Input, Textarea, Badge, RadioGroup, Field, FieldLabel, FieldDescription, FieldError,
    Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator,
    AlertDialog, AlertDialogPopup, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter,
    Dialog, DialogPopup, DialogHeader, DialogTitle, DialogDescription, DialogPanel, DialogFooter,
    Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent,
    MenuItem, MenuGroupLabel, Avatar,
  } = window.OriginDesignSystem_7c998b;
  /* The breadcrumb needs the product's router, and PageHead is rendered by every
     screen without it. The shell registers it here once per render; Crumbs reads
     it unless a caller passes onNavigate explicitly. */
  let breadcrumbNavigate = null;
  const registerBreadcrumbNavigate = (fn) => { breadcrumbNavigate = fn; };


  /* One threshold table for deadline pressure, product-wide. Tone is about the
     clock, never about the record's status. */
  const TIME_TONE_DAYS = { danger: 30, warning: 120 };
  const timeTone = (daysLeft) => (daysLeft <= TIME_TONE_DAYS.danger ? "danger" : daysLeft <= TIME_TONE_DAYS.warning ? "warning" : "neutral");
  const toneSuffix = (tone) => (tone === "neutral" ? "" : " cd-tb--" + tone);
  const clockClass = (tone) => "cd-clock" + (tone === "neutral" ? "" : " cd-clock--" + tone);

  /* The burn-down track. Elapsed share of the window, left to right, so the
     deadline is always the right-hand wall. */
  function TimeBar({ startAt, endAt, now, mini, label }) {
    const span = Math.max(1, endAt - startAt);
    const elapsed = Math.min(1, Math.max(0, (now - startAt) / span));
    const daysLeft = Math.round((endAt - now) / 864e5);
    return (
      <span className={"cd-tb" + (mini ? " cd-tb--mini" : "") + toneSuffix(timeTone(daysLeft))} role="img" aria-label={label}>
        <span className="cd-tb__fill" style={{ width: (elapsed * 100).toFixed(1) + "%" }} />
      </span>
    );
  }

  /* The claim's clock, spelled one way everywhere: bar plus countdown. */
  function ClaimClock({ claim, now, mini }) {
    const endAt = CLAIM_FLAGS.timeBarAt(claim);
    const daysLeft = CLAIM_FLAGS.daysLeft(claim, now);
    return (
      <span className="cd-cell-clock">
        <span className={clockClass(timeTone(daysLeft))} title={formatAbsolute(endAt)}>{formatCountdown(endAt, now)}</span>
        <TimeBar startAt={claim.deliveredAt} endAt={endAt} now={now} mini={mini} label={"Time bar: " + formatCountdown(endAt, now)} />
      </span>
    );
  }

  const resolvePath = (path, params) => (params ? path.replace(":id", params.id) : path);

  /* Breadcrumb only. The trail comes from the route table; params substitute
     the record's own id into the pattern the table holds. */
  function Crumbs({ path, params, labels, onNavigate }) {
    const trail = breadcrumb(path, CLAIMS_ROUTES);
    const go = onNavigate || breadcrumbNavigate;
    return (
      <Breadcrumb>
        <BreadcrumbList>
          {trail.map((seg, i) => {
            const href = resolvePath(seg.path, params);
            const label = (labels && labels[seg.path]) || seg.label;
            return (
              <React.Fragment key={seg.path}>
                {i > 0 ? <BreadcrumbSeparator /> : null}
                <BreadcrumbItem>
                  {i === trail.length - 1
                    ? <BreadcrumbPage>{label}</BreadcrumbPage>
                    : seg.isNamespace ? <span>{label}</span> : <BreadcrumbLink href={"#" + href} onSelect={() => go && go(href)}>{label}</BreadcrumbLink>}
                </BreadcrumbItem>
              </React.Fragment>
            );
          })}
        </BreadcrumbList>
      </Breadcrumb>
    );
  }

  /* `owner` is passed only by a screen that is the person's own desk rather than
     a shared destination — the face says whose queue this is, and is decoration
     on every other screen. Initials track the name, so a removed photo still
     renders the right person. */
  function PageHead({ path, params, labels, title, lede, actions, owner }) {
    return (
      <header className="cd-page-head">
        <div className="cd-page-head__lead">
          {owner ? (
            <Avatar size={56} src={owner.avatar || undefined} alt="" fallback={(owner.name || "").trim().split(/\s+/).slice(0, 2).map((w) => w[0] || "").join("").toUpperCase() || "?"} />
          ) : null}
          <div className="cd-page-head__text">
            <Crumbs path={path} params={params} labels={labels} />
            <h1 className="cd-title">{title}</h1>
            {lede ? <p className="cd-lede">{lede}</p> : null}
          </div>
        </div>
        {actions ? <div className="cd-page-head__actions">{actions}</div> : null}
      </header>
    );
  }

  /* The record header for the three claim screens: identity, status, the clock
     band, and the cargo line. The same block on all three, so the tabs change
     the surface below and nothing else. */
  function RecordHead({ claim, now, crumb, actions, children }) {
    const endAt = CLAIM_FLAGS.timeBarAt(claim);
    const daysLeft = CLAIM_FLAGS.daysLeft(claim, now);
    return (
      <div className="cd-head-record">
        <div className="cd-head-record__top">
          <div className="cd-page-head__text">
            {crumb}
            <span className="cd-head-record__id">
              <span className="cd-code">{claim.id}</span>
              <StatusBadge values={CLAIM_STATUS} value={claim.status} />
            </span>
            <h1 className="cd-title">
              <button type="button" className="cd-title-link" onClick={() => breadcrumbNavigate && breadcrumbNavigate("/claimants/" + claimantOf(CLAIMANTS, claim).id)}>{claim.customer}</button>
            </h1>
            <p className="cd-lede">{claim.incident}</p>
          </div>
          {actions ? <div className="cd-page-head__actions">{actions}</div> : null}
        </div>
        <div className="cd-head-record__bar">
          <TimeBar startAt={claim.deliveredAt} endAt={endAt} now={now} label={"Time bar: " + formatCountdown(endAt, now)} />
          <div className="cd-head-record__left">
            <div className={clockClass(timeTone(daysLeft))} title={formatAbsolute(endAt)} style={{ fontSize: "var(--text-sm)", fontWeight: "var(--font-weight-medium)" }}>{formatCountdown(endAt, now)}</div>
            <div className="cd-figure__note">to the time bar</div>
          </div>
          <div className="cd-head-record__ends">
            <span>Delivered {formatAbsolute(claim.deliveredAt).split(",")[0]}</span>
            <span>Time bar {formatAbsolute(endAt).split(",")[0]}</span>
          </div>
        </div>
        <div className="cd-facts">
          <span><strong>{claim.container}</strong> · {claim.commodity}</span>
          <span>{claim.pol} → {claim.pod}</span>
          <span>{claim.vessel} {claim.voyage}</span>
          <span>B/L <span className="cd-code">{claim.bl}</span></span>
        </div>
        {children}
      </div>
    );
  }

  function Figure({ label, value, note, xl }) {
    return (
      <div>
        <div className={xl ? "cd-figure cd-figure--xl" : "cd-figure"}>{value}</div>
        <div className="cd-figure__label">{label}</div>
        {note ? <div className="cd-figure__note">{note}</div> : null}
      </div>
    );
  }

  /* Evidence at a glance: one pip per document, in checklist order. */
  function DocumentPips({ documents }) {
    return (
      <span className="cd-pips" aria-hidden="true">
        {DOCUMENT_TYPES.map((type) => {
          const found = documents.find((d) => d.type === type.value);
          const status = found ? found.status : "requested";
          return <span key={type.value} className={"cd-pip cd-pip--" + status} />;
        })}
      </span>
    );
  }

  /* Irreversible only. Named consequence in the title, one action to confirm. */
  function ConfirmDialog({ request, onCancel, onConfirm }) {
    return (
      <AlertDialog open={!!request} onOpenChange={(open) => { if (!open) onCancel(); }}>
        <AlertDialogPopup>
          <AlertDialogHeader>
            <AlertDialogTitle>{request ? request.title : ""}</AlertDialogTitle>
            <AlertDialogDescription>{request ? request.description : ""}</AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <Button variant="outline" onClick={onCancel}>Cancel</Button>
            <Button variant={request && request.destructive ? "destructive" : "default"} onClick={onConfirm}>{request ? request.confirmLabel : ""}</Button>
          </AlertDialogFooter>
        </AlertDialogPopup>
      </AlertDialog>
    );
  }

  const parseMoney = (text) => {
    const cleaned = String(text).replace(/[^0-9.]/g, "");
    if (!cleaned) return NaN;
    return Math.round(parseFloat(cleaned) * 100);
  };
  const moneyInputValue = (cents) => (cents === null || cents === undefined || isNaN(cents) ? "" : (cents / 100).toFixed(2));

  /* A staged dialog — its own commit model, so an instant screen may open it.
     fields: [{ id, label, kind: "money"|"text"|"radio", options, hint, placeholder, optional }] */
  function FormDialog({ request, onCancel, onSubmit }) {
    /* One state object keyed by the request it belongs to. Resetting in an
       effect leaves one paint where `values` is still {} but `validate` has
       already been called with it, and resetting with setState during render is
       no better — the rest of that render still runs on the old values. So the
       fresh entry is computed here and used immediately. */
    const blank = (req) => ({ req: req, values: (req && req.initial) || {}, touched: false, submitted: false });
    const [entry, setEntry] = React.useState(() => blank(request));
    const current = entry.req === request ? entry : blank(request);
    if (entry.req !== request) setEntry(current);
    if (!request) return null;
    const values = current.values, touched = current.touched, submitted = current.submitted;
    const errors = request.validate ? request.validate(values) : {};
    const valid = Object.keys(errors).length === 0;
    const set = (id, value) => setEntry((e) => Object.assign({}, e, { touched: true, values: Object.assign({}, e.values, { [id]: value }) }));
    const markSubmitted = () => setEntry((e) => Object.assign({}, e, { submitted: true }));
    return (
      <Dialog open onOpenChange={(open) => { if (!open) onCancel(); }}>
        <DialogPopup>
          <DialogHeader>
            <DialogTitle>{request.title}</DialogTitle>
            <DialogDescription>{request.description}</DialogDescription>
          </DialogHeader>
          <DialogPanel>
            <div className="cd-col cd-col--tight">
              {request.fields.filter((f) => !f.when || f.when(values)).map((field) => (
                <Field key={field.id}>
                  <FieldLabel htmlFor={"cd-f-" + field.id}>{field.label}</FieldLabel>
                  {field.kind === "radio" ? (
                    <RadioGroup items={field.options} value={values[field.id] || ""} onValueChange={(v) => set(field.id, v)} name={"cd-f-" + field.id} />
                  ) : field.kind === "text" ? (
                    <Textarea id={"cd-f-" + field.id} rows={4} placeholder={field.placeholder} value={values[field.id] || ""} invalid={submitted && !!errors[field.id]} onChange={(e) => set(field.id, e.target.value)} />
                  ) : (
                    <Input id={"cd-f-" + field.id} inputMode="decimal" startAddon="$" placeholder={field.placeholder} value={values[field.id] || ""} invalid={submitted && !!errors[field.id]} onChange={(e) => set(field.id, e.target.value)} />
                  )}
                  {submitted && errors[field.id]
                    ? <FieldError>{errors[field.id]}</FieldError>
                    : field.hint ? <FieldDescription>{field.hint}</FieldDescription> : null}
                </Field>
              ))}
            </div>
          </DialogPanel>
          <DialogFooter>
            {touched ? <Button variant="outline" onClick={onCancel}>Cancel</Button> : null}
            <Button disabled={!touched} onClick={() => { markSubmitted(); if (valid) onSubmit(values); }}>{request.submitLabel}</Button>
          </DialogFooter>
        </DialogPopup>
      </Dialog>
    );
  }

  /* One export entry point, the same in every page header that has one. */
  function ExportMenu({ scope, onExport }) {
    return (
      <Floating
        align="end" width="12rem" label="Export"
        trigger={<Button variant="outline" size="sm"><Icon name="download" />Export<Icon name="chevron-down" /></Button>}
      >
        {(close) => (
          <>
            <MenuGroupLabel>{scope}</MenuGroupLabel>
            {["CSV", "XLSX"].map((format) => (
              <MenuItem key={format} onClick={() => { close(); onExport(format); }}>{format}</MenuItem>
            ))}
          </>
        )}
      </Floating>
    );
  }

  function RouteNotBuilt({ path, onNavigate }) {
    const route = matchRoute(path, CLAIMS_ROUTES);
    return (
      <div className="cd-page">
        <PageHead path={path} title={route ? route.label : "Not found"} />
        <Empty>
          <EmptyHeader>
            <EmptyMedia variant="icon"><Icon name="route" size={18} /></EmptyMedia>
            <EmptyTitle>{route ? route.label + " is registered, not built" : "There is nothing at " + path}</EmptyTitle>
            <EmptyDescription>{route ? "The route and its place in the palette are real; the screen behind it is not part of this template." : "This path is not in the claims desk's route table, so it has no breadcrumb and nothing links to it. The four destinations in the bar, plus the claim, recovery and account screens, are the whole product."}</EmptyDescription>
          </EmptyHeader>
          <EmptyContent>
            <Button variant="outline" onClick={() => onNavigate("/")}>Back to the work queue</Button>
          </EmptyContent>
        </Empty>
      </div>
    );
  }

  Object.assign(window, {
    registerBreadcrumbNavigate,
    TIME_TONE_DAYS, timeTone, clockClass, TimeBar, ClaimClock, Crumbs, PageHead, RecordHead, Figure, DocumentPips,
    ConfirmDialog, FormDialog, ExportMenu, RouteNotBuilt, parseMoney, moneyInputValue, resolvePath,
  });
})();
