/* Marque Talent Desk — shared parts, local to this product. Reported in README
   as local additions: Portrait / Who (creator identity, squared, one geometry),
   BookingBar / Availability (the 90-day availability device), PlatformLadder,
   DueClock (the forward-looking date column), Ledger rows, FormDialog, and the
   small repeated pieces. Nothing here holds data or assigns a status tone —
   StatusBadge does that. */
(() => {
  const {
    Button, Input, Textarea, Badge, RadioGroup, Select, 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,
    Avatar, MenuItem, MenuGroupLabel,
  } = 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; };


  /* Audience numbers are read, not counted: 1.4M, 412K. One spelling, wherever
     a follower count appears. */
  const compact = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
  const formatFollowers = (n) => compact.format(n);

  /* The availability horizon. Ninety days is how far ahead brands ask us to
     hold someone, so it is the axis every booking bar is drawn on. */
  const HORIZON_DAYS = 90;

  function Eyebrow({ children }) {
    return <span className="mq-eyebrow">{children}</span>;
  }

  function Figure({ label, value, note, size }) {
    return (
      <div className={"mq-figure" + (size ? " mq-figure--" + size : "")}>
        <Eyebrow>{label}</Eyebrow>
        <div className="mq-figure__value">{value}</div>
        {note ? <div className="mq-figure__note">{note}</div> : null}
      </div>
    );
  }

  /* Platform marks. Brand-coloured source files, so they are drawn as images
     rather than inline icons; height is fixed and width follows the mark's own
     aspect ratio. */
  const PLATFORM_MARKS = {
    instagram: "../../assets/templates/icons/instagram.svg",
    tiktok: "../../assets/templates/icons/tiktok.svg",
    youtube: "../../assets/templates/icons/youtube.svg",
  };
  function PlatformIcon({ platform, size = 14 }) {
    const src = PLATFORM_MARKS[platform];
    if (!src) return null;
    return <img className="mq-mark" src={src} alt="" aria-hidden="true" style={{ height: size + "px" }} />;
  }
  /* Platform options carry their mark. Built here so the select trigger and the
     open list read identically. */
  const platformItems = () => PLATFORMS.map((p) => ({
    value: p.value,
    label: <span className="mq-mark__row"><PlatformIcon platform={p.value} />{p.label}</span>,
  }));

  /* Creator identity. Squared, never a circle, and always at a size the source
     image can actually carry. */
  function Portrait({ creator, size = 40 }) {
    return <Avatar className="mq-portrait" size={size} src={creator.portrait} alt="" fallback={creator.name.split(" ").map((w) => w[0]).join("")} />;
  }

  function Who({ creator, size = 32, meta, onClick }) {
    return (
      <span className="mq-who">
        <Portrait creator={creator} size={size} />
        <span className="mq-who__text">
          {onClick
            ? <button type="button" className="mq-link mq-who__name" onClick={onClick}>{creator.name}</button>
            : <span className="mq-who__name">{creator.name}</span>}
          <span className="mq-who__meta">{meta || "@" + creator.handle}</span>
        </span>
      </span>
    );
  }

  /* The booking bar: the next ninety days, filled for the part of it the
     creator is not sellable. Free creators show an empty track, which is the
     point — the roster reads as a strip of open and closed weeks. */
  function BookingBar({ creator, now, label }) {
    const span = HORIZON_DAYS * 864e5;
    const until = creator.status === "booked" ? creator.bookedUntil : creator.status === "paused" ? creator.pausedUntil : null;
    const share = until ? Math.min(1, Math.max(0, (until - now) / span)) : 0;
    const tone = creator.status === "paused" ? "paused" : creator.status === "available" ? "free" : CREATOR_FLAGS.isComingFree(creator, now) ? "soon" : "";
    return (
      <span className={"mq-bookbar" + (tone ? " mq-bookbar--" + tone : "")} role="img" aria-label={label}>
        <span className="mq-bookbar__fill" style={{ width: (share * 100).toFixed(1) + "%" }} />
      </span>
    );
  }

  /* Status, the sentence that explains it, and the bar — spelled one way
     everywhere a creator's availability appears. */
  function Availability({ creator, now, deals, compactMode }) {
    const comingFree = CREATOR_FLAGS.isComingFree(creator, now);
    const blocked = unavailableBecause(creator, deals, now);
    const words = creator.status === "available"
      ? "Free to pitch today"
      : comingFree
        ? "Free in " + CREATOR_FLAGS.daysUntilFree(creator, now) + " days — " + blocked.replace("Booked to ", "with ")
        : blocked;
    return (
      <div className="mq-col mq-col--tight">
        <div className="mq-stack">
          <StatusBadge values={CREATOR_STATUS} value={creator.status} />
          {comingFree ? <Badge size="sm" variant="warning">Coming free</Badge> : null}
        </div>
        <BookingBar creator={creator} now={now} label={words} />
        {compactMode ? null : (
          <>
            <div className="mq-bookbar__ends"><span>Today</span><span>+{HORIZON_DAYS}d</span></div>
            <span className="mq-note">{words}</span>
          </>
        )}
      </div>
    );
  }

  function PlatformLadder({ creator }) {
    return (
      <div className="mq-plat">
        {creator.platforms.slice().sort((a, b) => b.followers - a.followers).map((p) => (
          <div className="mq-plat__row" key={p.platform}>
            <span className="mq-plat__label">{labelOf(PLATFORMS, p.platform)}</span>
            <span className="mq-plat__n">{formatFollowers(p.followers)}</span>
            <span className="mq-plat__e">{formatPercent(p.engagementRate)}</span>
          </div>
        ))}
        <div className="mq-plat__row">
          <span className="mq-plat__label">Total</span>
          <span className="mq-plat__n">{formatFollowers(reachOf(creator))}</span>
          <span className="mq-plat__e">{formatPercent(engagementOf(creator))}</span>
        </div>
      </div>
    );
  }

  /* A due date is a forward-looking column: direction is the first thing the
     reader needs, so the countdown spelling is primary and the absolute date is
     on hover. Delivered work has no countdown — it left on time or it didn't,
     and the row says which. */
  function DueClock({ item, now }) {
    const overdue = DELIVERABLE_FLAGS.isOverdue(item, now);
    const soon = DELIVERABLE_FLAGS.isDueSoon(item, now);
    /* Handed over: the countdown is spent, so the cell states the date it was
       due rather than a distance that would read as if it were still running. */
    if (DELIVERABLE_FLAGS.isDelivered(item)) {
      return <span className="mq-mono mq-muted" title={formatAbsolute(item.dueAt)}>Due {formatAbsolute(item.dueAt).split(",")[0]}</span>;
    }
    return (
      <span className={"mq-mono" + (overdue ? " mq-bad" : soon ? " mq-warn" : " mq-muted")} title={formatAbsolute(item.dueAt)}>
        {formatCountdown(item.dueAt, now)}
      </span>
    );
  }

  function KindList({ deliverables }) {
    return (
      <div className="mq-kinds">
        {deliverables.map((line) => (
          <Badge key={line.kind} variant="secondary" size="sm">{line.quantity} × {labelOf(DELIVERABLE_KINDS, line.kind)}</Badge>
        ))}
      </div>
    );
  }

  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, and a namespace
     parent with no screen renders as plain text. */
  function Crumbs({ path, params, labels, onNavigate }) {
    const trail = breadcrumb(path, MARQUE_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>
    );
  }

  function PageHead({ path, params, labels, eyebrow, title, lede, actions }) {
    return (
      <header className="mq-head">
        <div className="mq-head__text">
          {eyebrow ? <Eyebrow>{eyebrow}</Eyebrow> : <Crumbs path={path} params={params} labels={labels} />}
          <h1 className="mq-title">{title}</h1>
          {lede ? <p className="mq-lede">{lede}</p> : null}
        </div>
        {actions ? <div className="mq-head__actions">{actions}</div> : null}
      </header>
    );
  }

  /* Record header for the two record screens: the crumb, the identifier, the
     status, the name at display size. */
  function RecordHead({ crumb, id, status, title, lede, actions, media, children }) {
    return (
      <header className="mq-head">
        {media ? <div className="mq-head__media">{media}</div> : null}
        <div className="mq-head__text">
          {crumb}
          <div className="mq-stack"><span className="mq-mono">{id}</span>{status}</div>
          <h1 className="mq-title mq-title--sm">{title}</h1>
          {lede ? <p className="mq-lede">{lede}</p> : null}
          {children}
        </div>
        {actions ? <div className="mq-head__actions">{actions}</div> : null}
      </header>
    );
  }

  function LedgerRow({ label, hint, value, variant }) {
    return (
      <div className={"mq-lg__row" + (variant ? " mq-lg__row--" + variant : "")}>
        <span className="mq-lg__label">{label}{hint ? <span className="mq-lg__hint">{hint}</span> : null}</span>
        <span className="mq-lg__value">{value}</span>
      </div>
    );
  }

  /* 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 parseCount = (text) => {
    const cleaned = String(text).replace(/[^0-9]/g, "");
    return cleaned ? parseInt(cleaned, 10) : NaN;
  };
  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"|"count"|"text"|"radio"|"select",
                options, hint, placeholder, when }] */
  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="mq-col mq-col--tight">
              {request.fields.filter((f) => !f.when || f.when(values)).map((field) => (
                <Field key={field.id}>
                  <FieldLabel htmlFor={"mq-f-" + field.id}>{field.label}</FieldLabel>
                  {field.kind === "radio" ? (
                    <RadioGroup items={field.options} value={values[field.id] || ""} onValueChange={(v) => set(field.id, v)} name={"mq-f-" + field.id} />
                  ) : field.kind === "select" ? (
                    <Select id={"mq-f-" + field.id} items={field.options} value={values[field.id] || ""} placeholder={field.placeholder} onValueChange={(v) => set(field.id, v)} />
                  ) : field.kind === "text" ? (
                    <Textarea id={"mq-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)} />
                  ) : field.kind === "count" ? (
                    <Input id={"mq-f-" + field.id} inputMode="numeric" placeholder={field.placeholder} value={values[field.id] || ""} invalid={submitted && !!errors[field.id]} onChange={(e) => set(field.id, e.target.value)} />
                  ) : (
                    <Input id={"mq-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={() => { setSubmitted(true); 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, MARQUE_ROUTES);
    return (
      <div className="mq-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 Marque's route table, so it has no breadcrumb and nothing links to it. The five destinations in the bar, plus the brief, deal and account screens, are the whole product."}</EmptyDescription>
          </EmptyHeader>
          <EmptyContent>
            <Button variant="outline" onClick={() => onNavigate("/")}>Back to the roster</Button>
          </EmptyContent>
        </Empty>
      </div>
    );
  }

  Object.assign(window, {
    registerBreadcrumbNavigate,
    HORIZON_DAYS, formatFollowers, Eyebrow, Figure, Portrait, Who, BookingBar, Availability, PlatformLadder,
    PlatformIcon, platformItems, DueClock, KindList, Crumbs, PageHead, RecordHead, LedgerRow, ConfirmDialog, FormDialog, ExportMenu,
    RouteNotBuilt, parseMoney, parseCount, moneyInputValue, resolvePath,
  });
})();
