/* Marketing Desk — the parts the record screens share with the tiled ones.
   A Band is the card a record screen groups content in; MdCards.jsx holds the
   tile grid the dashboard-shaped screens use. Both take the same surface, the
   same hairline and the same one shadow, so the product reads as one thing. Nothing here holds data or
   assigns a status tone: StatusBadge does the tone, metrics.js does the
   arithmetic, enums.js decides which transitions exist. Reported in the README
   as local additions to the design system's inventory. */
(() => {
  const {
    Button, Input, Select, Checkbox, Textarea, Field, FieldLabel, FieldDescription, FieldError,
    Dialog, DialogPopup, DialogHeader, DialogTitle, DialogDescription, DialogPanel, DialogFooter,
    AlertDialog, AlertDialogPopup, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter,
    RadioGroup, MenuItem, MenuGroupLabel, Badge,
    Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent,
  } = window.OriginDesignSystem_7c998b;

  /* ── Bands and figures: the pane's only structural devices ─────────────── */
  function Band({ title, note, actions, flush, tight, className, children }) {
    return (
      <section className={"md-band" + (flush ? " md-band--flush" : "") + (tight ? " md-band--tight" : "") + (className ? " " + className : "")}>
        {title || actions ? (
          <div className="md-band__head">
            <div>
              <h2 className="md-band__title">{title}</h2>
              {note ? <p className="md-band__note">{note}</p> : null}
            </div>
            {actions ? <div className="md-page-header__actions">{actions}</div> : null}
          </div>
        ) : null}
        {children}
      </section>
    );
  }

  /* Four figures to a row, or three where the record has three. Every one
     carries a qualifier that decomposes its own figure's set. */
  function Figures({ items, three }) {
    return (
      <div className={"md-figures" + (three ? " md-figures--3" : "")}>
        {items.map((item) => (
          <div key={item.label}>
            <span className="md-figure__label">{item.label}</span>
            <span className="md-figure">{item.value}</span>
            <span className="md-figure__qualifier">{item.qualifier}</span>
          </div>
        ))}
      </div>
    );
  }

  /* ── The day chart ─────────────────────────────────────────────────────
     Every bar is a figure, so every bar carries its own tooltip with the numbers
     it is drawn from. The tooltip anchor is the grid item, so it has to stretch
     or the percentage heights inside resolve against a content-sized box. */
  function DayChart({ days, label, legend, foot }) {
    const max = days.reduce((n, d) => Math.max(n, d.total), 0) || 1;
    const every = Math.max(1, Math.ceil(days.length / 6));
    return (
      <div className="md-chartbox">
        <div className="md-chart" role="img" aria-label={label}>
          {days.map((day, i) => (
            <FloatingTip key={day.at} side="bottom" label={
              <span className="md-tipgrid">
                <span style={{ gridColumn: "1 / -1", fontWeight: 500 }}>{new Date(day.at).toLocaleDateString("en-GB", { weekday: "short", day: "numeric", month: "short" })}</span>
                {day.tip.map((line) => (
                  <React.Fragment key={line.label}>
                    <span style={{ opacity: 0.7 }}>{line.label}</span>
                    <span style={{ textAlign: "right" }}>{line.value}</span>
                  </React.Fragment>
                ))}
              </span>
            }>
              <span className="md-chart__col" aria-hidden="true">
                {day.total === 0 ? <span className="md-chart__seg md-chart__seg--empty" /> : day.segments.filter((s) => s.value > 0).slice().reverse().map((s) => (
                  <span key={s.key} className={"md-chart__seg" + (s.tone ? " md-chart__seg--" + s.tone : "")} style={{ height: (s.value / max) * 100 + "%" }} />
                ))}
              </span>
            </FloatingTip>
          ))}
        </div>
        <div className="md-chart__axis" aria-hidden="true">
          {days.map((day, i) => (
            <span key={day.at}>{i % every === 0 ? new Date(day.at).toLocaleDateString("en-GB", { day: "numeric", month: "short" }) : ""}</span>
          ))}
        </div>
        <div className="md-chart__foot">
          {legend ? (
            <span className="md-legend">
              {legend.map((item) => (
                <span className="md-legend__item" key={item.label}>
                  <span className={"md-legend__swatch" + (item.tone ? " md-legend__swatch--" + item.tone : "")} />
                  {item.label}
                </span>
              ))}
            </span>
          ) : <span />}
          {foot ? <span className="md-footnote">{foot}</span> : null}
        </div>
      </div>
    );
  }

  /* A proportion, with the words above it. Never a box. */
  function Meter({ label, value, parts }) {
    return (
      <div className="md-meter">
        {label ? <span className="md-footnote">{label}</span> : null}
        <span className="md-bar" role="img" aria-label={label}>
          {parts.map((p) => <span key={p.key} className={"md-bar__fill" + (p.tone ? " md-bar__fill--" + p.tone : "")} style={{ width: Math.max(0, Math.min(100, p.percent)) + "%" }} />)}
        </span>
      </div>
    );
  }

  /* ── Actions ───────────────────────────────────────────────────────────
     The set comes from enums.js; this draws it, and states the reason where the
     set says the user cannot take it yet. */
  function Transitions({ transitions, onRun, size = "sm" }) {
    if (!transitions.length) return null;
    return (
      <span className="md-actions__row">
        {transitions.map((t) => {
          const button = (
            <Button key={t.id} size={size} disabled={!!t.disabled}
              variant={t.destructive ? "destructive-outline" : t.primary ? "default" : "outline"}
              onClick={() => onRun(t)}>{t.label}</Button>
          );
          return t.disabled && t.disabledReason
            ? <FloatingTip key={t.id} label={t.disabledReason} side="bottom">{button}</FloatingTip>
            : button;
        })}
      </span>
    );
  }

  /* A row carries the transition its status asks for — the primary one — as a
     button. Everything else goes in the row's menu: a list is not where you put
     an action that cannot be undone. */
  function RowActions({ transitions, onRun, label }) {
    const primary = transitions.filter((t) => t.primary);
    const rest = transitions.filter((t) => !t.primary);
    if (!primary.length && !rest.length) return <span className="md-muted">—</span>;
    return (
      <span className="md-actions__row">
        {primary.length ? <Transitions transitions={primary} onRun={onRun} /> : null}
        {rest.length ? (
          <Floating align="end" width="13rem" label={label}
            trigger={<Button variant="ghost" size="icon-sm" aria-label={label}><Icon name="ellipsis" /></Button>}>
            {(close) => rest.map((t) => (
              <MenuItem key={t.id} disabled={!!t.disabled} variant={t.destructive ? "destructive" : "default"} onClick={() => { close(); onRun(t); }}>{t.label}</MenuItem>
            ))}
          </Floating>
        ) : null}
      </span>
    );
  }

  /* ── List furniture ────────────────────────────────────────────────────── */
  function FilterBar({ query, onQuery, placeholder, selects, onClear, dirty }) {
    return (
      <div className="md-filters">
        <div className="md-filters__text">
          <Input size="sm" value={query} placeholder={placeholder} aria-label={placeholder} startAddon={<Icon name="search" />} onChange={(e) => onQuery(e.target.value)} />
        </div>
        {selects.map((s) => (
          <span className="md-filters__select" key={s.id}>
            <Select size="sm" items={s.items} value={s.value} aria-label={s.label} onValueChange={s.onChange} />
          </span>
        ))}
        {dirty ? <Button variant="ghost" size="sm" onClick={onClear}>Clear filters</Button> : null}
      </div>
    );
  }

  function BulkBar({ count, note, children, onClear }) {
    if (!count) return null;
    return (
      <div className="md-bulk">
        <span className="md-bulk__count">{count} selected</span>
        {note ? <span className="md-footnote">{note}</span> : null}
        <span className="md-bulk__actions">
          {children}
          <Button variant="ghost" size="sm" onClick={onClear}>Clear selection</Button>
        </span>
      </div>
    );
  }

  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>
    );
  }

  /* ── Dialogs ───────────────────────────────────────────────────────────── */
  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="destructive" onClick={onConfirm}>{request ? request.confirmLabel : ""}</Button>
          </AlertDialogFooter>
        </AlertDialogPopup>
      </AlertDialog>
    );
  }

  /* A staged dialog — its own commit model, so an instant screen may open one. */
  function FormDialog({ request, onCancel, onSubmit }) {
    const [values, setValues] = React.useState({});
    const [touched, setTouched] = React.useState(false);
    const [submitted, setSubmitted] = React.useState(false);
    React.useEffect(() => { setValues(request ? request.initial || {} : {}); setTouched(false); setSubmitted(false); }, [request]);
    if (!request) return null;
    const errors = request.validate ? request.validate(values) : {};
    const valid = Object.keys(errors).length === 0;
    const set = (id, value) => { setTouched(true); setValues((v) => Object.assign({}, v, { [id]: value })); };
    return (
      <Dialog open onOpenChange={(open) => { if (!open) onCancel(); }}>
        <DialogPopup>
          <DialogHeader>
            <DialogTitle>{request.title}</DialogTitle>
            <DialogDescription>{request.description}</DialogDescription>
          </DialogHeader>
          <DialogPanel>
            <div className="md-fields">
              {request.fields.filter((f) => !f.when || f.when(values)).map((field) => (
                <Field key={field.id}>
                  <FieldLabel htmlFor={"md-f-" + field.id}>{field.label}</FieldLabel>
                  {field.kind === "radio" ? (
                    <RadioGroup name={"md-f-" + field.id} items={field.options} value={values[field.id] || ""} onValueChange={(v) => set(field.id, v)} />
                  ) : field.kind === "select" ? (
                    <Select items={field.options} value={values[field.id] || ""} aria-label={field.label} onValueChange={(v) => set(field.id, v)} />
                  ) : field.kind === "text" ? (
                    <Textarea id={"md-f-" + field.id} rows={3} placeholder={field.placeholder} value={values[field.id] || ""} invalid={submitted && !!errors[field.id]} onChange={(e) => set(field.id, e.target.value)} />
                  ) : (
                    <Input id={"md-f-" + field.id} 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={() => { setSubmitted(true); if (valid) onSubmit(values); }}>{request.submitLabel}</Button>
          </DialogFooter>
        </DialogPopup>
      </Dialog>
    );
  }

  /* Not a route and not an empty state: the fallback for a hand-typed or stale
     path, which has no breadcrumb because breadcrumbs are made of the route
     table. */
  function RouteNotBuilt({ path, onNavigate }) {
    return (
      <div className="md-band">
        <Empty>
          <EmptyHeader>
            <EmptyMedia variant="icon"><Icon name="signpost" size={18} /></EmptyMedia>
            <EmptyTitle>Nothing is registered at {path}</EmptyTitle>
            <EmptyDescription>Marketing Desk registers twelve destinations and the screens behind them. The route table decides what exists, so this address has no screen behind it.</EmptyDescription>
          </EmptyHeader>
          <EmptyContent><Button onClick={() => onNavigate("/")}>Go to Overview</Button></EmptyContent>
        </Empty>
      </div>
    );
  }

  /* The staged form bar every settings screen ends with. Sticky to the bottom of
     the pane, which is the scroll owner. */
  function FormBar({ dirty, note, onCancel, onSave, label }) {
    return (
      <div className="md-formbar">
        <span className="md-formbar__note">{note}</span>
        {dirty ? <Button variant="outline" onClick={onCancel}>Cancel</Button> : null}
        <Button disabled={!dirty} onClick={onSave}>{label}</Button>
      </div>
    );
  }

  /* Sorting: one hook, one header control, one comparator, so every sortable
     table in the product behaves identically. The accessor map is what keeps the
     comparator from knowing anything about the domain. */
  function useSort(key, dir) {
    const [sort, setSort] = React.useState({ key: key, dir: dir || "desc" });
    const onSort = (next) => setSort((s) => (s.key === next ? { key: next, dir: s.dir === "asc" ? "desc" : "asc" } : { key: next, dir: "desc" }));
    return { sort: sort, onSort: onSort };
  }

  function SortHead({ label, sortKey, sort, onSort }) {
    const on = sort.key === sortKey;
    return (
      <button type="button" className="md-sort" data-on={on ? "true" : "false"}
        aria-label={"Sort by " + label + (on && sort.dir === "asc" ? ", highest first" : ", lowest first")}
        onClick={() => onSort(sortKey)}>
        {label}
        <Icon name={on ? (sort.dir === "asc" ? "arrow-up" : "arrow-down") : "chevrons-up-down"} size={12} />
      </button>
    );
  }

  function sortRows(rows, sort, accessors) {
    const get = accessors[sort.key];
    if (!get) return rows;
    return rows.slice().sort((a, b) => {
      const av = get(a), bv = get(b);
      if (typeof av === "string" || typeof bv === "string") {
        const cmp = String(av == null ? "" : av).localeCompare(String(bv == null ? "" : bv));
        return sort.dir === "asc" ? cmp : -cmp;
      }
      const an = av == null ? -Infinity : av, bn = bv == null ? -Infinity : bv;
      return sort.dir === "asc" ? an - bn : bn - an;
    });
  }

  function KindChip({ children }) {
    return <Badge size="sm" variant="secondary">{children}</Badge>;
  }

  Object.assign(window, { Band, Figures, DayChart, Meter, Transitions, RowActions, FilterBar, BulkBar, ExportMenu, ConfirmDialog, FormDialog, FormBar, RouteNotBuilt, KindChip, useSort, SortHead, sortRows });
})();
