/* page-messenger.jsx — Messenger module (03-ui §2). IIFE-scoped; exports via window. */
(function(){
const { useState, useMemo } = React;

// ── Compose form initial state ──────────────────────────────────────────────
function _blankForm(role, overrides) {
  return {
    id: "new",
    type: "",
    title: "",
    body: "",
    audience: role === "OM" ? "outlet"
            : role === "AM" ? "area"
            : role === "CM" ? "country"
            : "all",                // GA default = all
    country_id: null,
    area_id: null,
    outlet_ids: [],
    status: "draft",
    scheduled_at: null,
    sent_at: null,
    archived_at: null,
    author: { tier: role, scope: null },
    recipient_rollup: { sent: 0, read: 0, failed: 0 },
    _schedDate: "",
    _schedTime: "",
    _submitMode: "now",  // "now" | "schedule"
    ...(overrides || {}),
  };
}

/**
 * @spec GA-053 / US-053 Messenger
 * Ops module — all four roles (GA/CM/AM/OM) can send notifications scoped to their tier.
 * GA: global scope + selection/BulkBar/Export/hard-delete drafts.
 * CM: country-scoped; AM: area-scoped; OM: outlet-scoped.
 */
function MessengerList({ role = "GA", scope = {} }) {
  if (!["GA", "CM", "AM", "OM"].includes(role)) return _notAuthorised;

  const isGA = role === "GA";
  const isCM = role === "CM";
  const isAM = role === "AM";
  const isOM = role === "OM";

  // ── Simulated role scope ────────────────────────────────────────────────
  // Use existing mock-scope constants for the role persona.
  const CM_COUNTRY_ID = "PH";
  const AM_AREA_ID_VAL = "ar_03";   // avoid collision with the bare AM_AREA_ID const in mock-data
  const OM_OUTLET_ID_VAL = "SH-PH-001";

  // ── State ─────────────────────────────────────────────────────────────────
  const [filters, setFilters] = useState({});
  // Scope-driven DateTimeFilter — null ⇒ Today · Full day (shared SSOT for inline + drawer).
  const [dtf, setDtf] = useState(null);
  // GeoScope (country→area→outlet) — non-OM only (spec Table 1). Null ⇒ no scope filter.
  const [geo, setGeo] = useState(null);
  const [searchQuery, setSearchQuery] = useState("");
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(20);
  const [selected, setSelected] = useState({});
  const [drawerFor, setDrawerFor] = useState(null);   // notif id | "new"
  const [form, setForm] = useState(null);
  const [editing, setEditing] = useState(false);
  const [confirmArchive, setConfirmArchive] = useState(false);
  const [confirmDelete, setConfirmDelete] = useState(false);
  const [toast, setToast] = useState(null);
  // Operator remarks (Ops-module convention) — edit-own-only, keyed per notification id.
  const [remarksByNotif, setRemarksByNotif] = useState({});
  const addNotifRemark = (id) => (content) => {
    const entry = { id: "rem_" + Math.random().toString(36).slice(2, 10), content, created_by_name: "You", created_by: "you", created_at: new Date().toISOString().replace("T", " ").slice(0, 16) };
    setRemarksByNotif(m => ({ ...m, [id]: [...(m[id] || []), entry] }));
    _pushAudit({ action: "remark_added", entity_type: "messenger", entity_id: id, diff: { content } });
  };
  const editNotifRemark = (id) => (remarkId, content) => {
    setRemarksByNotif(m => ({ ...m, [id]: (m[id] || []).map(r => r.id === remarkId ? { ...r, content } : r) }));
    _pushAudit({ action: "remark_edited", entity_type: "messenger", entity_id: id, diff: { id: remarkId, content } });
  };
  const deleteNotifRemark = (id) => (remarkId) => {
    setRemarksByNotif(m => ({ ...m, [id]: (m[id] || []).filter(r => r.id !== remarkId) }));
    _pushAudit({ action: "remark_deleted", entity_type: "messenger", entity_id: id, diff: { id: remarkId } });
  };

  const fireToast = (msg) => { setToast(msg); setTimeout(() => setToast(null), 2400); };

  // ── Scope-filtered + status-filtered rows ──────────────────────────────────
  const allRows = useMemo(() => {
    return SB_NOTIFICATIONS.filter(n => {
      // Role scoping
      if (isCM) {
        if (n.audience === "all") return false;
        if (n.country_id !== CM_COUNTRY_ID) return false;
      }
      if (isAM) {
        if (n.audience === "all" || n.audience === "country") return false;
        if (n.audience === "area" && n.area_id !== AM_AREA_ID_VAL) return false;
        if (n.audience === "outlet") {
          const areaOutlets = SB3_OUTLETS.filter(o => o.area_id === AM_AREA_ID_VAL).map(o => o.id);
          if (!(n.outlet_ids || []).some(id => areaOutlets.includes(id))) return false;
        }
      }
      if (isOM) {
        if (n.audience !== "outlet") return false;
        if (!(n.outlet_ids || []).includes(OM_OUTLET_ID_VAL)) return false;
      }
      return true;
    });
  }, [role]);

  const rows = useMemo(() => {
    const includeArchived = filters.include_archived === "yes";
    return allRows.filter(n => {
      // Default: exclude archived unless filter says otherwise
      if (!includeArchived && n.status === "archived") return false;
      if (filters.status && n.status !== filters.status) return false;
      if (filters.type && n.type !== filters.type) return false;
      if (!isOM && filters.audience_scope && n.audience !== filters.audience_scope) return false;
      // GeoScope (non-OM): match the notification's discrete scope fields.
      if (!isOM && geo) {
        if (geo.countries && geo.countries.length && !geo.countries.includes(n.country_id)) return false;
        if (geo.areas && geo.areas.length) {
          // area-targeted row matches directly; outlet-targeted row matches via its outlets' area.
          const rowAreas = new Set();
          if (n.area_id) rowAreas.add(n.area_id);
          (n.outlet_ids || []).forEach(id => { const o = SB3_OUTLET_BY_ID[id]; if (o) rowAreas.add(o.area_id); });
          if (![...rowAreas].some(a => geo.areas.includes(a))) return false;
        }
        if (geo.outlets && geo.outlets.length && !(n.outlet_ids || []).some(id => geo.outlets.includes(id))) return false;
      }
      // DateTimeFilter — shared scope-tz-aware predicate, ANDed with the above.
      // Only applied to rows that HAVE a timestamp (sent_at or scheduled_at);
      // drafts/unsent rows with neither stay visible (a record with no time can't
      // be in/out of a time window — hiding them would wrongly drop draft work).
      const ts = n.sent_at || n.scheduled_at;
      if (ts && !_dtfMatch(dtf, ts, n.country_id || "SG")) return false;
      if (searchQuery) {
        const q = searchQuery.toLowerCase();
        const hay = [n.title, n.body, NOTIF_TYPE_LABELS[n.type] || n.type].join(" ").toLowerCase();
        if (!hay.includes(q)) return false;
      }
      return true;
    });
  }, [allRows, filters, dtf, geo, searchQuery]);

  const totalPages = Math.max(1, Math.ceil(rows.length / pageSize));
  const visibleRows = rows.slice((page - 1) * pageSize, page * pageSize);

  // ── Selection (GA only) ────────────────────────────────────────────────────
  const selectedIds = Object.keys(selected).filter(k => k !== "all" && selected[k]);
  const selectedCount = selected.all ? rows.length : selectedIds.length;
  const onToggleSelected = (id) => {
    if (id === "all") {
      setSelected(s => s.all ? {} : { all: true });
    } else {
      setSelected(s => { const n = { ...s, [id]: !s[id] }; delete n.all; return n; });
    }
  };
  const clearSelection = () => setSelected({});
  const effectiveSelectedIds = selected.all ? rows.map(r => r.id) : selectedIds;
  const selectedDraftCount = effectiveSelectedIds.filter(id => (SB_NOTIFICATIONS.find(n => n.id === id) || {}).status === "draft").length;

  // ── Drawer helpers ─────────────────────────────────────────────────────────
  const openDrawer = (row) => {
    setDrawerFor(row.id);
    setForm({ ...row, _schedDate: "", _schedTime: "", _submitMode: "now" });
    setEditing(false);
  };
  const createNew = () => {
    setDrawerFor("new");
    setForm(_blankForm(role));
    setEditing(true);
  };
  const closeDrawer = () => { setDrawerFor(null); setForm(null); setEditing(false); };
  const setF = (k, v) => setForm(f => ({ ...f, [k]: v }));

  // ── Compose actions ───────────────────────────────────────────────────────
  const onSaveDraft = () => {
    _pushAudit({ action: "notification_save_draft", entity_type: "notifications", entity_id: form.id, diff: form });
    closeDrawer();
    fireToast("Draft saved");
  };
  const onSendNow = () => {
    _pushAudit({ action: "notification_send", entity_type: "notifications", entity_id: form.id, diff: { status: "sent" } });
    closeDrawer();
    fireToast("Notification sent");
  };
  const onSchedule = () => {
    if (!form._schedDate || !form._schedTime) { fireToast("Set a date and time to schedule."); return; }
    const scheduled_at = `${form._schedDate} ${form._schedTime}`;
    _pushAudit({ action: "notification_schedule", entity_type: "notifications", entity_id: form.id, diff: { status: "scheduled", scheduled_at } });
    closeDrawer();
    fireToast(`Scheduled for ${scheduled_at}`);
  };
  const onUnschedule = () => {
    _pushAudit({ action: "notification_unschedule", entity_type: "notifications", entity_id: form.id, diff: { status: "draft", scheduled_at: null } });
    setF("status", "draft");
    setF("scheduled_at", null);
    fireToast("Unscheduled — moved to draft");
  };
  const onDuplicateAsNew = () => {
    setDrawerFor("new");
    setForm(_blankForm(role, { type: form.type, title: `Copy of ${form.title}`, body: form.body, audience: form.audience, country_id: form.country_id, area_id: form.area_id, outlet_ids: form.outlet_ids }));
    setEditing(true);
    fireToast("Duplicated as new draft");
  };

  // ── Bulk actions (GA only) ─────────────────────────────────────────────────
  const bulkArchive = () => {
    _pushAudit({ action: "notification_bulk_archive", entity_type: "notifications", entity_id: null, diff: { ids: effectiveSelectedIds } });
    setConfirmArchive(false); clearSelection();
    fireToast(`${effectiveSelectedIds.length} notification(s) archived`);
  };
  const bulkDelete = () => {
    _pushAudit({ action: "notification_bulk_delete", entity_type: "notifications", entity_id: null, diff: { ids: effectiveSelectedIds.filter(id => (SB_NOTIFICATIONS.find(n => n.id === id) || {}).status === "draft") } });
    setConfirmDelete(false); clearSelection();
    fireToast("Draft notification(s) deleted");
  };
  const bulkExport = () => {
    fireToast(`Exporting ${effectiveSelectedIds.length > 0 ? effectiveSelectedIds.length : rows.length} notification(s)…`);
  };

  // ── Audience scope options per role ────────────────────────────────────────
  const audienceOptions = useMemo(() => {
    if (isGA) {
      return [
        { value: "all",     label: "All markets" },
        { value: "country", label: "Country" },
        { value: "area",    label: "Area" },
        { value: "outlet",  label: "Outlet" },
      ];
    }
    if (isCM) {
      return [
        { value: "country", label: `Country · ${CM_COUNTRY_ID}` },
        { value: "area",    label: "Area" },
        { value: "outlet",  label: "Outlet" },
      ];
    }
    if (isAM) {
      return [
        { value: "area",   label: "Area (own)" },
        { value: "outlet", label: "Outlet (in area)" },
      ];
    }
    // OM: outlet only
    return [
      { value: "outlet", label: "Outlet (own)" },
    ];
  }, [role]);

  // Country/area/outlet scope entity options for compose audience select
  const countryOptions = useMemo(() =>
    SB2_COUNTRIES.map(c => ({ value: c.country_id, label: `${c.flag} ${c.country_name}` })),
  []);
  const areaOptionsForCM = useMemo(() =>
    SB3_AREAS.filter(a => a.country_id === CM_COUNTRY_ID).map(a => ({ value: a.id, label: a.name })),
  []);
  const areaOptionsForGA = useMemo(() =>
    SB3_AREAS.map(a => ({ value: a.id, label: a.name })),
  []);
  const outletOptions = useMemo(() => {
    if (isGA) return SB3_OUTLETS.map(o => ({ value: o.id, label: o.name }));
    if (isCM) return SB3_OUTLETS.filter(o => o.country_id === CM_COUNTRY_ID).map(o => ({ value: o.id, label: o.location }));
    if (isAM) return SB3_OUTLETS.filter(o => o.area_id === AM_AREA_ID_VAL).map(o => ({ value: o.id, label: o.location }));
    return SB3_OUTLETS.filter(o => o.id === OM_OUTLET_ID_VAL).map(o => ({ value: o.id, label: o.location }));
  }, [role]);

  // ── Columns ───────────────────────────────────────────────────────────────
  const baseColumns = [
    {
      label: "Title", sortable: true, sortKey: "title",
      render: r => <span style={{ ...T.primary() }}>{r.title || "Untitled"}</span>,
    },
    {
      label: "Type", width: 110, sortable: true, sortKey: "type",
      render: r => <span style={{ ...T_MUTED }}>{NOTIF_TYPE_LABELS[r.type] || r.type}</span>,
    },
    {
      label: "Audience", width: 170, sortable: true, sortKey: "audience",
      render: r => <span style={{ ...T_MUTED }}>{_notifAudienceLabel(r)}</span>,
    },
    {
      label: "Status", width: 110, sortable: true, sortKey: "status",
      render: r => <StatusPill status={r.status} kind="notification"/>,
    },
    {
      label: "Sent / Sched.", width: 140, sortable: true, sortKey: "sent_at",
      render: r => <span style={{ ...T_MUTED }}>{_fmtDateTime(r.sent_at || r.scheduled_at, r.country_id || "SG")}</span>,
    },
  ];
  const gaRollupColumn = {
    label: "Recipients", width: 90, sortable: true, sortKey: "recipient_rollup",
    render: r => <span style={{ ...T_MUTED }}>
      {r.recipient_rollup && r.recipient_rollup.sent > 0 ? r.recipient_rollup.sent.toLocaleString() : "—"}
    </span>,
  };
  const columns = isGA ? [...baseColumns, gaRollupColumn] : baseColumns;

  // ── Filter config ──────────────────────────────────────────────────────────
  const statusOptions = [
    { value: "", label: "All statuses" },
    { value: "draft", label: "Draft" },
    { value: "scheduled", label: "Scheduled" },
    { value: "sent", label: "Sent" },
    { value: "failed", label: "Failed" },
  ];
  const typeOptions = [
    { value: "", label: "All types" },
    ...Object.entries(NOTIF_TYPE_LABELS).map(([k, v]) => ({ value: k, label: v })),
  ];

  // Scope resolvers — Messenger is multi-country, so pass the RAW scope (some
  // "all markets" rows carry country_id:null → "SG" fallback at row-match time).
  const _dtfTz = _scopeTz(scope);
  const _dtfTod = useMemo(
    () => _scopePresets(scope, typeof CUSTOM_OPTIONS !== "undefined" ? CUSTOM_OPTIONS : []),
    [scope]
  );

  const advancedFilters = [
    // Audience scope + GeoScope are non-OM only (OM is fixed to own outlet — spec Table 1).
    ...(isOM ? [] : [
      { key: "audience_scope", kind: "select", label: "Audience scope",
        options: [{ value: "", label: "All" }, ...audienceOptions] },
      { key: "geo", kind: "geoscope", label: "Network scope", role,
        countries: SB2_COUNTRIES.map(c => ({ value: c.country_id, label: `${c.flag} ${c.country_name}` })),
        areas:     SB3_AREAS.map(a => ({ value: a.id, label: a.name, country_id: a.country_id })),
        outlets:   SB3_OUTLETS.map(o => ({ value: o.id, label: o.location || o.name, area_id: o.area_id, country_id: o.country_id })) },
    ]),
    // Date + time-of-day is ONE DateTimeFilter (shared `dtf` state) — surfaced
    // BOTH inline (dateTimeFilter prop) AND here in the drawer; both edit `dtf`.
    { key: "dtf", kind: "datetime", label: "Date & time", country: _dtfTz, todOptions: _dtfTod },
    { key: "include_archived", kind: "select", label: "Archived",
      options: [{ value: "", label: "Exclude archived" }, { value: "yes", label: "Include archived" }] },
  ];

  // ── Compose form validation ────────────────────────────────────────────────
  const canSubmit = form && form.title.trim() && form.body.trim() && form.type && form.audience;

  // ── Delivery rollup (GA + sent only) ──────────────────────────────────────
  const renderDelivery = (notif) => {
    if (notif.status !== "sent") return null;
    const rollup = notif.recipient_rollup || {};
    return (
      <FormSection title="General">
        <StatPanel cols={3}>
          <StatCard
            label="Sent"
            value={(rollup.sent || 0).toLocaleString()}
            sub="Total recipients reached"/>
          <StatCard
            label="Read"
            value={(rollup.read || 0).toLocaleString()}
            sub={rollup.sent > 0 ? `${Math.round((rollup.read / rollup.sent) * 100)}% open rate` : "—"}/>
          <StatCard
            label="Failed"
            value={(rollup.failed || 0).toLocaleString()}
            sub="Delivery failures"/>
        </StatPanel>
      </FormSection>
    );
  };

  // ── Drawer view-mode actions by status×role ────────────────────────────────
  const renderDrawerViewActions = () => {
    if (!form || editing) return null;
    const { status } = form;
    const canEdit = !["sent", "failed", "archived"].includes(status);
    return (
      <>
        {status === "failed" && (
          <Btn variant="secondary" onClick={onDuplicateAsNew}>Duplicate as new</Btn>
        )}
        {status === "scheduled" && (
          <Btn variant="secondary" onClick={onUnschedule}>Unschedule</Btn>
        )}
        {canEdit && (
          <Btn variant="primary" onClick={() => setEditing(true)}>Edit</Btn>
        )}
      </>
    );
  };

  // ── Audience scope entity selector (compose) ───────────────────────────────
  const renderAudienceScopeField = () => {
    if (!form) return null;
    const aud = form.audience;
    if (aud === "all") return null;
    if (aud === "country") {
      if (isGA) {
        return (
          <Field label="Country" required>
            <Select value={form.country_id || ""} onChange={v => setF("country_id", v || null)} placeholder="Select country">
              {countryOptions.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
            </Select>
          </Field>
        );
      }
      // CM — locked to own country
      const c = SB2_COUNTRY_BY_ID[CM_COUNTRY_ID];
      return (
        <Field label="Country">
          <Input value={c ? `${c.flag} ${c.country_name}` : CM_COUNTRY_ID} disabled/>
        </Field>
      );
    }
    if (aud === "area") {
      if (isGA || isCM) {
        // GA = any area; CM = areas within own country
        const opts = isGA ? areaOptionsForGA : areaOptionsForCM;
        return (
          <Field label="Area" required>
            <Select value={form.area_id || ""} onChange={v => setF("area_id", v || null)} placeholder="Select area">
              {opts.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
            </Select>
          </Field>
        );
      }
      // AM — locked to own area
      const a = SB3_AREA_BY_ID[AM_AREA_ID_VAL];
      return (
        <Field label="Area">
          <Input value={a ? a.name : AM_AREA_ID_VAL} disabled/>
        </Field>
      );
    }
    if (aud === "outlet") {
      if (isOM) {
        const o = SB3_OUTLET_BY_ID[OM_OUTLET_ID_VAL];
        return (
          <Field label="Outlet">
            <Input value={o ? o.location : OM_OUTLET_ID_VAL} disabled/>
          </Field>
        );
      }
      return (
        <Field label="Outlet" required>
          <Select value={(form.outlet_ids || [])[0] || ""} onChange={v => setF("outlet_ids", v ? [v] : [])} placeholder="Select outlet">
            {outletOptions.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
          </Select>
        </Field>
      );
    }
    return null;
  };

  // ── Compose submit footer ──────────────────────────────────────────────────
  const renderComposeFooter = () => {
    if (!form || !editing) return null;
    return (
      <FormSection title="Send">
        <Field label="Delivery mode">
          <SegToggle
            value={form._submitMode}
            onChange={v => setF("_submitMode", v)}
            options={[
              { value: "now",      label: "Send now" },
              { value: "schedule", label: "Schedule" },
            ]}
            ariaLabel="Delivery mode"/>
        </Field>
        {form._submitMode === "schedule" && (
          <div style={{ display: "flex", gap: 12 }}>
            <Field label="Date" required style={{ flex: 1 }}>
              <DateField value={form._schedDate} onChange={v => setF("_schedDate", v)}/>
            </Field>
            <Field label="Time" required style={{ flex: 1 }}>
              <Input type="time" value={form._schedTime} onChange={v => setF("_schedTime", v)}/>
            </Field>
          </div>
        )}
      </FormSection>
    );
  };

  // ── Page description ──────────────────────────────────────────────────────
  const description = isGA
    ? "Send and manage push notifications across all markets."
    : isCM ? "Send notifications to customers in your country."
    : isAM ? "Send notifications to customers in your area."
    : "Send notifications to customers at your outlet.";

  // ── Compose drawer title ───────────────────────────────────────────────────
  const drawerTitle = form
    ? (form.id === "new" ? "New notification" : (form.title || "Notification"))
    : "";

  return (
    <div className="page-inner">
      <PageHead
        title="Messenger"
        description={description}
        actions={<><ExportButton role={role} variant="secondary" onClick={() => fireToast("Exporting notifications…")}/><Btn variant="primary" onClick={createNew}>New message</Btn></>}/>

      <FilterBar
        primaryFilter={[
          { key: "status", label: "Status", options: statusOptions },
          { key: "type",   label: "Type",   options: typeOptions   },
        ]}
        primaryValue={{ status: filters.status || "", type: filters.type || "" }}
        onPrimaryChange={next => { setFilters(f => ({ ...f, ...next })); setPage(1); }}
        advancedFilters={advancedFilters}
        advancedValues={{ ...filters, dtf, geo }}
        onAdvancedChange={(k, v) => {
          if (k === "dtf") { setDtf(v); }       // inline + drawer share ONE dtf state
          else if (k === "geo") { setGeo(v); }
          else { setFilters(f => ({ ...f, [k]: v })); }
          setPage(1);
        }}
        onAdvancedReset={() => { setFilters(f => ({ status: f.status || "", type: f.type || "" })); setDtf(null); setGeo(null); }}
        onReset={() => { setFilters({}); setSearchQuery(""); setDtf(null); setGeo(null); setPage(1); }}
        search={searchQuery}
        onSearch={v => { setSearchQuery(v); setPage(1); }}
        searchPlaceholder="Search notifications…"/>

      {isGA && (
        <BulkBar
          selectedCount={selectedCount}
          onDeselect={clearSelection}
          actions={
            <>
              <Btn variant="ghost" onClick={bulkExport}>Export</Btn>
              <Btn variant="ghost" onClick={() => setConfirmArchive(true)}>Archive</Btn>
              <Btn variant="danger" onClick={() => setConfirmDelete(true)} disabled={selectedDraftCount === 0}>Delete drafts</Btn>
            </>
          }/>
      )}

      <Table
        emptyText="No notifications match these filters."
        columns={columns}
        rows={visibleRows}
        onRow={openDrawer}
        {...(isGA ? { selected, onToggleSelected } : {})}/>

      <TableFooter
        page={page}
        totalPages={totalPages}
        onPage={setPage}
        count={pageSize}
        onCountChange={c => { setPageSize(c); setPage(1); }}/>

      {/* ── Detail / Compose Drawer ────────────────────────────────────────── */}
      <DetailDrawer
        open={!!drawerFor}
        onClose={closeDrawer}
        title={drawerTitle}
        actions={
          form && (editing ? (
            <>
              <Btn variant="secondary" onClick={() => form.id === "new" ? closeDrawer() : setEditing(false)}>Cancel</Btn>
              <Btn variant="secondary" onClick={onSaveDraft} disabled={!canSubmit}>Save as draft</Btn>
              {form._submitMode === "schedule"
                ? <Btn variant="primary" onClick={onSchedule} disabled={!canSubmit}>Schedule</Btn>
                : <Btn variant="primary" onClick={onSendNow} disabled={!canSubmit}>Send now</Btn>}
            </>
          ) : renderDrawerViewActions())
        }
        width={700}>

        {form && !editing && (
          <>
            {/* Inline alerts in body (§14) */}
            {form.status === "failed" && (
              <InlineAlert kind="error">Delivery failed. Duplicate as a new draft to retry.</InlineAlert>
            )}
            {form.status === "scheduled" && (
              <InlineAlert kind="info">Scheduled for {form.scheduled_at}. Click Unschedule to move back to draft.</InlineAlert>
            )}

            <FormSection title="General">
              <KeyValueGrid items={[
                { label: "Status",   value: <StatusPill status={form.status} kind="notification"/> },
                { label: "Title",    value: form.title || "—" },
                { label: "Type",     value: NOTIF_TYPE_LABELS[form.type] || form.type || "—" },
                { label: "Message",  value: form.body || "—" },
                { label: "Author",   value: form.author ? `${form.author.tier} · ${form.author.scope || "all"}` : "—" },
              ]}/>
            </FormSection>

            <FormSection title="Targeting">
              <KeyValueGrid items={[
                { label: "Audience", value: _notifAudienceLabel(form) },
                { label: "Country",
                  value: form.country_id
                    ? `${(SB2_COUNTRY_BY_ID[form.country_id] || {}).flag || ""} ${(SB2_COUNTRY_BY_ID[form.country_id] || {}).country_name || form.country_id}`.trim()
                    : (form.audience === "all" ? "All markets" : "—") },
                { label: "Area",
                  value: form.area_id ? ((SB3_AREA_BY_ID[form.area_id] || {}).name || form.area_id) : "—" },
                { label: "Outlets",
                  value: (form.outlet_ids && form.outlet_ids.length)
                    ? form.outlet_ids.map(id => (SB3_OUTLET_BY_ID[id] || {}).location || id).join(", ")
                    : "—" },
              ]}/>
            </FormSection>

            {form.scheduled_at && (
              <FormSection title="Schedule">
                <KeyValueGrid items={[
                  { label: "Scheduled at", value: _fmtDateTime(form.scheduled_at, form.country_id || "SG") },
                ]}/>
              </FormSection>
            )}

            {/* Delivery — only when status=sent; snapshot variant (no state/onChange) */}
            {form.status === "sent" && renderDelivery(form)}

            <FormSection title="Meta">
              <KeyValueGrid items={[
                { label: "Created at", value: _fmtDateTime(form.created_at, form.country_id || "SG") },
                { label: "Sent at",  value: _fmtDateTime(form.sent_at, form.country_id || "SG") },
                { label: "Archived", value: form.archived_at || "—" },
              ]}/>
            </FormSection>

            <RemarksSection entityType="messenger" entityId={form.id} remarks={remarksByNotif[form.id] || []} onAdd={addNotifRemark(form.id)} currentUser="you" onEdit={editNotifRemark(form.id)} onDelete={deleteNotifRemark(form.id)} role={role}/>
          </>
        )}

        {form && editing && (
          <>
            <FormSection title="General">
              <Field label="Title" hint="Maximum 60 characters." required>
                <Input
                  value={form.title}
                  onChange={v => setF("title", v.slice(0, 60))}
                  placeholder="e.g. Loyalty Week — double points all week"
                  maxLength={60}/>
                <div style={{ ...T_MUTED, fontSize: 12, textAlign: "right", marginTop: 2 }}>{form.title.length}/60</div>
              </Field>
              <Field label="Message" hint="Maximum 160 characters." required>
                <Textarea
                  value={form.body}
                  onChange={v => setF("body", v.slice(0, 160))}
                  placeholder="Notification message visible to customers."
                  rows={4}
                  maxLength={160}/>
                <div style={{ ...T_MUTED, fontSize: 12, textAlign: "right", marginTop: 2 }}>{form.body.length}/160</div>
              </Field>
            </FormSection>

            <FormSection title="Type">
              <Field label="Type" required>
                <Select value={form.type} onChange={v => setF("type", v)} placeholder="Select type">
                  <option value="">Select type…</option>
                  {Object.entries(NOTIF_TYPE_LABELS).map(([k, v]) =>
                    <option key={k} value={k}>{v}</option>
                  )}
                </Select>
              </Field>
            </FormSection>

            <FormSection title="Audience">
              <Field label="Audience" required>
                <Select
                  value={form.audience}
                  onChange={v => {
                    setF("audience", v);
                    // Reset scope fields when audience changes
                    setForm(f => ({ ...f, audience: v, country_id: null, area_id: null, outlet_ids: [] }));
                  }}
                  disabled={isOM || (isCM && false) || (isAM && false)}>
                  {audienceOptions.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                </Select>
              </Field>
              {renderAudienceScopeField()}
            </FormSection>

            {renderComposeFooter()}
          </>
        )}
      </DetailDrawer>

      {/* ── Modals ──────────────────────────────────────────────────────────── */}
      <ConfirmModal
        open={confirmArchive}
        title="Archive selected notifications?"
        body={<div>{`Archive ${effectiveSelectedIds.length} notification(s)? They won't be deleted but will be hidden by default.`}</div>}
        confirmLabel="Archive"
        onCancel={() => setConfirmArchive(false)}
        onConfirm={bulkArchive}/>

      <ConfirmModal
        open={confirmDelete}
        title="Delete draft notifications?"
        body={<div>{`${selectedDraftCount} draft notification(s) will be permanently deleted. Scheduled, sent, failed, and archived notifications are not affected.`}</div>}
        confirmLabel="Delete drafts"
        onCancel={() => setConfirmDelete(false)}
        onConfirm={bulkDelete}/>

      <Toast message={toast || ""} show={!!toast}/>
    </div>
  );
}

Object.assign(window, { MessengerList });
})();
