/* page-translations.jsx — extracted verbatim from pages.jsx. IIFE-scoped; exports via window. */
(function(){
const { useState, useMemo, useEffect, useRef } = React;
/**
 * @spec GA-080 — Translation matrix
 * US-090 only. String-key × locale grid. Publish writes to audit_logs,
 * propagates to consumer app on next fetch. Uses SearchableTranslationGrid
 * primitive. Spec-03 has no generic `ui_translations` — logged as OQ-034.
 *
 * Note: US-092 (default language per country) is delivered on GA-021
 * via the `default_language` Select — not on this screen.
 */
function TranslationMatrix({ role = "GA" }) {
  const [filters, setFilters] = useState({ namespace: "", locale: "", missing: false, geo: { countries: [] } });
  const [searchQuery, setSearchQuery] = useState("");
  // SB5 rectification · M-2 — lift draft to window so GA-081 preview can read it
  const [draft, setDraft] = useState(
    (typeof window !== "undefined" && window.__sixhands_translations_draft) || SB5_TRANSLATION_VALUES_PUBLISHED
  );
  const [confirmOpen, setConfirmOpen] = useState(false);
  const [toast, setToast] = useState(null);
  if (role !== "GA") return _notAuthorised;

  const allKeys = SB5_TRANSLATION_KEYS;
  const keysByNs = filters.namespace
    ? allKeys.filter(k => k.startsWith(filters.namespace + "."))
    : allKeys;
  const visibleKeys = filters.missing
    ? keysByNs.filter(k => SB5_LOCALES.some(l => !(draft[k] && draft[k][l])))
    : keysByNs;
  const visibleLocales = filters.locale ? [filters.locale] : SB5_LOCALES;

  // Diff published vs draft.
  const changedKeys = allKeys.filter(k => {
    const p = SB5_TRANSLATION_VALUES_PUBLISHED[k] || {};
    const d = draft[k] || {};
    return SB5_LOCALES.some(l => (p[l] || "") !== (d[l] || ""));
  });
  const hasUnsaved = changedKeys.length > 0;
  const missingCount = allKeys.reduce((acc, k) => acc + SB5_LOCALES.filter(l => !(draft[k] && draft[k][l])).length, 0);

  const onEdit = (k, l, v) => setDraft(prev => {
    const next = { ...prev, [k]: { ...(prev[k] || {}), [l]: v } };
    // M-2 · mirror edits into window for GA-081 preview draft source
    if (typeof window !== "undefined") window.__sixhands_translations_draft = next;
    return next;
  });

  const onPublish = () => {
    _pushAudit({
      actor_user_id: "u_ga_01", role: "GA", action: "translation.publish",
      entity_type: "translations", entity_id: `bundle_v${43}`,
      diff: { before: { version: 42 }, after: { version: 43, keys_changed: changedKeys.length } },
    });
    // M-2 · clear the draft on publish so preview reverts to Published parity
    if (typeof window !== "undefined") window.__sixhands_translations_draft = null;
    setConfirmOpen(false);
    setToast(`Published · ${changedKeys.length} key(s) · logged to audit_logs`);
    setTimeout(() => setToast(null), 3000);
  };

  return (
    <div className="page-inner">
      <PageHead title="Translations" description="Localise consumer-facing content across supported languages."
        actions={
          <div className="row ai-c" style={{ gap: 10 }}>
            <span style={{ ...T_MUTED }}>
              {missingCount > 0
                ? `${missingCount} missing · ${changedKeys.length} unsaved`
                : `${changedKeys.length} unsaved`}
            </span>
            <Btn variant="ghost" onClick={() => _go("ga-translations-preview")}>Preview</Btn>
            <Btn variant="primary" disabled={!hasUnsaved} onClick={() => setConfirmOpen(true)}>
              Publish…
            </Btn>
          </div>
        }/>

      {hasUnsaved && (
        <InlineAlert kind="warn">
          Draft changes pending · <strong>{changedKeys.length}</strong> key(s) differ from the published bundle.
          Consumer app continues to serve the published bundle until Publish writes to <strong>audit_logs</strong>.
        </InlineAlert>
      )}

      <FilterBar
        primaryFilter={{ key: "namespace", label: "Namespace", options: [{ value: "", label: "All namespaces" }, ...SB5_NAMESPACES.map(n => ({ value: n.id, label: n.label }))] }}
        primaryValue={filters.namespace}
        onPrimaryChange={v => setFilters(f => ({ ...f, namespace: v }))}
        advancedFilters={[
          { key: "locale", label: "Locale", kind: "select", options: [{ value: "", label: "All locales" }, ...SB5_LOCALES.map(l => ({ value: l, label: SB5_LOCALE_LABEL[l] || l }))] },
          { key: "geo", kind: "geoscope", label: "Network scope", role,
            countries: SB2_COUNTRIES.map(co => ({ value: co.country_id, label: `${co.flag} ${co.country_name}` })) },
        ]}
        advancedValues={{ locale: filters.locale, geo: filters.geo }}
        onAdvancedChange={(k, v) => setFilters(f => ({ ...f, [k]: v }))}
        onAdvancedReset={() => setFilters(f => ({ ...f, locale: "", geo: { countries: [] } }))}
        onReset={() => setFilters({ namespace: "", locale: "", missing: false, geo: { countries: [] } })}
        rightActions={
          <ChipToggle on={filters.missing} onToggle={() => setFilters(f => ({ ...f, missing: !f.missing }))}>
            Missing only
          </ChipToggle>
        }
        search={searchQuery}
        onSearch={setSearchQuery}
        searchPlaceholder="Search strings…"/>

      <SearchableTranslationGrid
        keys={visibleKeys}
        locales={visibleLocales}
        values={draft}
        onEdit={onEdit}
        filter=""/>

      {visibleKeys.length === 0 && (
        <EmptyState icon={null}
          title="No keys match these filters"
          body="Try clearing the missing-only toggle or switching namespace."/>
      )}

      <ConfirmModal
        open={confirmOpen}
        title={`Publish ${changedKeys.length} translation change${changedKeys.length === 1 ? "" : "s"}?`}
        body={
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <div>Publishing writes a new bundle to <strong>audit_logs</strong> and increments the translation version.</div>
            <div>Translations propagate to the <strong>consumer app</strong> on its next fetch (typically within 5 minutes).</div>
            <div>This cannot be undone from the UI; rollback requires DX.</div>
          </div>
        }
        confirmLabel="Publish bundle"
        onCancel={() => setConfirmOpen(false)}
        onConfirm={onPublish}/>

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

/**
 * @spec GA-081 — Translation preview
 * US-091. Two-column preview of strings in-situ. Device chrome via
 * DevicePreviewFrame. Draft vs published toggle mirrors GA-080.
 */
function TranslationPreview({ role = "GA" }) {
  const [locale, setLocale]   = useState(SB5_LOCALES[0]);
  const [screen, setScreen]   = useState("consumer.home");
  const [surface, setSurface] = useState("consumer"); // consumer | admin
  const [source, setSource]   = useState("published"); // published | draft
  if (role !== "GA") return _notAuthorised;

  // M-2 · draft vs published — draft bundle lives on window, set by GA-080
  const bundle = source === "draft"
    ? ((typeof window !== "undefined" && window.__sixhands_translations_draft) || SB5_TRANSLATION_VALUES_PUBLISHED)
    : SB5_TRANSLATION_VALUES_PUBLISHED;
  const t = (k) => (bundle[k] && bundle[k][locale]) || "";
  const isMissing = (k) => !t(k);

  const SCREEN_OPTIONS = [
    { value: "consumer.home",         label: "Consumer · Home" },
    { value: "consumer.order",        label: "Consumer · Order" },
    { value: "consumer.order-confirm",label: "Consumer · Order confirmation" },
    { value: "admin.dashboard",       label: "Admin · Dashboard" },
    { value: "admin.shell",           label: "Admin · Shell" },
  ];

  // M-5 / m-10 · replaced inline chip with StatusPill kind="translation"
  const MissingPill = () => (
    <span style={{ marginLeft: 8, display: "inline-block", verticalAlign: "middle" }}>
      <StatusPill status="missing" kind="translation"/>
    </span>
  );

  const renderConsumerHome = () => (
    <div style={{ padding: 14, display: "flex", flexDirection: "column", gap: 12, height: "100%" }}>
      <div style={{ font: "700 18px/1.2 var(--font)", color: "var(--forest)" }}>
        {t("consumer.home.greeting") || <span style={{ color: "var(--mint)" }}>— greeting —</span>}{isMissing("consumer.home.greeting") && <MissingPill/>}
      </div>
      <div style={{ ...T_MUTED, fontSize: 13, fontWeight: 500, lineHeight: 1.4 }}>
        {t("consumer.home.today_picks") || "—"}
      </div>
      <div style={{ ...T_SOFT_CARD, padding: 12 }}>
        <div style={{ font: "700 12px/1 var(--font-ui)", color: "var(--orange)" }}>
          {t("consumer.home.reward_banner") || <span>— reward_banner —</span>}{isMissing("consumer.home.reward_banner") && <MissingPill/>}
        </div>
      </div>
      <div className="row ai-c jc-sb">
        <span style={{ font: "500 12px/1 var(--font-ui)", color: "var(--mint)" }}>{t("consumer.home.nearest_outlet") || "—"}</span>
        <span style={{ font: "700 12px/1 var(--font-ui)", color: "var(--forest)" }}>{t("consumer.home.loyalty_balance") || "—"} 248</span>
      </div>
      <div style={{ marginTop: "auto", padding: "12px 16px", borderRadius: "var(--r)", background: "var(--forest)", color: "var(--lime)", textAlign: "center", font: "700 14px/1 var(--font-ui)" }}>
        {t("consumer.home.hero_cta") || "— hero_cta —"}
      </div>
    </div>
  );
  const renderConsumerOrder = () => (
    <div style={{ padding: 14, display: "flex", flexDirection: "column", gap: 12, height: "100%" }}>
      <div style={{ font: "700 18px/1.2 var(--font)", color: "var(--forest)" }}>{t("consumer.order.cart_title") || "—"}</div>
      <div style={{ display: "flex", gap: 8 }}>
        <ChipToggle on size="sm">{t("consumer.order.pickup_label") || "—"}</ChipToggle>
        <ChipToggle size="sm">{t("consumer.order.delivery_label") || "—"}</ChipToggle>
      </div>
      <div style={{ ...T_MUTED, fontSize: 12 }}>{t("consumer.order.payment_method") || <><span>— payment_method —</span><MissingPill/></>}</div>
      <div style={{ ...T_MUTED, fontSize: 12 }}>{t("consumer.order.apply_promo") || "—"}</div>
      <div style={{ marginTop: "auto", padding: "12px 16px", borderRadius: "var(--r)", background: "var(--forest)", color: "var(--lime)", textAlign: "center", font: "700 14px/1 var(--font-ui)" }}>
        {t("consumer.order.checkout_cta") || "—"}
      </div>
    </div>
  );
  const renderConsumerOrderConfirm = () => (
    <div style={{ padding: 14, display: "flex", flexDirection: "column", gap: 10, height: "100%", justifyContent: "center", alignItems: "center" }}>
      <div style={{ font: "700 22px/1.2 var(--font)", color: "var(--forest)" }}>{t("consumer.order.order_placed") || "—"}</div>
      <div style={{ ...T_MUTED, fontSize: 13 }}>{t("consumer.order.receipt_title") || "—"} · #98221</div>
      <div style={{ ...T_MUTED, fontSize: 12 }}>{t("consumer.order.contact_support") || "—"}</div>
    </div>
  );
  const renderAdminShell = () => (
    <div style={{ padding: 14, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, height: "100%" }}>
      {["admin.shell.dashboard","admin.shell.countries","admin.shell.franchisees","admin.shell.outlets","admin.shell.users","admin.shell.payments","admin.shell.audit","admin.shell.profile"].map(k => (
        <div key={k} style={{ padding: "10px 12px", borderRadius: "var(--r-xs)", background: "var(--mint-soft)", color: "var(--forest)", font: "700 12px/1.2 var(--font-ui)" }}>
          {t(k) || <><span style={{ color: "var(--mint)" }}>— {k.split(".").pop()} —</span><MissingPill/></>}
        </div>
      ))}
    </div>
  );
  const renderAdminDashboard = () => (
    <div style={{ padding: 14, display: "flex", flexDirection: "column", gap: 10, height: "100%" }}>
      <div style={{ font: "700 16px/1.2 var(--font)", color: "var(--forest)" }}>{t("admin.shell.dashboard") || "—"}</div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
        <div style={{ ...T_SOFT_CARD, padding: 10 }}><div style={{ ...T.label, font: "700 11px/1 var(--font-ui)" }}>GMV</div><div style={{ font: "700 18px/1.2 var(--font)", color: "var(--forest)", marginTop: 4 }}>S$ 2.31M</div></div>
        <div style={{ ...T_SOFT_CARD, padding: 10 }}><div style={{ ...T.label, font: "700 11px/1 var(--font-ui)" }}>{t("admin.shell.outlets") || "—"}</div><div style={{ font: "700 18px/1.2 var(--font)", color: "var(--forest)", marginTop: 4 }}>14</div></div>
      </div>
    </div>
  );

  const screenBody =
    screen === "consumer.home"          ? renderConsumerHome() :
    screen === "consumer.order"         ? renderConsumerOrder() :
    screen === "consumer.order-confirm" ? renderConsumerOrderConfirm() :
    screen === "admin.dashboard"        ? renderAdminDashboard() :
                                           renderAdminShell();

  const device = surface === "consumer" ? "mobile" : "admin";
  const allowedScreens = surface === "consumer"
    ? SCREEN_OPTIONS.filter(o => o.value.startsWith("consumer."))
    : SCREEN_OPTIONS.filter(o => o.value.startsWith("admin."));

  return (
    <div className="page-inner">
      <PageHead title="Translation preview"
        back={{ label: "Translations", onClick: () => _go("ga-translations") }}/>

      {source === "draft" && (
        <InlineAlert kind="warn">
          Previewing the <strong>draft</strong> bundle. Consumer app continues to serve the published bundle until publish.
        </InlineAlert>
      )}

      <div style={{ display: "grid", gridTemplateColumns: "300px 1fr", gap: 24, alignItems: "start" }}>
        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          <Tabs
            tabs={[{ id: "consumer", label: "Consumer" }, { id: "admin", label: "Admin" }]}
            active={surface}
            onChange={(v) => {
              setSurface(v);
              setScreen(v === "consumer" ? "consumer.home" : "admin.dashboard");
            }}/>
          <Field label="Language">
            <Select value={locale} onChange={setLocale} placeholder="Pick a language">
              {SB5_LOCALES.map(l => <option key={l} value={l}>{SB5_LOCALE_LABEL[l] || l}</option>)}
            </Select>
          </Field>
          <Field label="Screen">
            <Select value={screen} onChange={setScreen} placeholder="Pick a screen">
              {allowedScreens.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
            </Select>
          </Field>
          <Field label="Source bundle">
            <div style={{ display: "flex", gap: 8 }}>
              <ChipToggle on={source === "published"} onToggle={() => setSource("published")}>Published</ChipToggle>
              <ChipToggle on={source === "draft"}     onToggle={() => setSource("draft")}>Draft</ChipToggle>
            </div>
          </Field>
          <InlineAlert kind="info">
            Preview renders strings from the selected bundle. Missing strings show an orange <em>missing</em> tag.
          </InlineAlert>
        </div>

        <div style={{ display: "flex", justifyContent: "center" }}>
          <DevicePreviewFrame device={device} locale={SB5_LOCALE_LABEL[locale] || locale}>
            {screenBody}
          </DevicePreviewFrame>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { TranslationMatrix, TranslationPreview });
})();
