/* page-catalog.jsx — Products (SKUCatalog). PartnerRewards + CMPartnerReward relocated to page-rewards.jsx.
   IIFE-scoped; exports via window. */
(function(){
const { useState, useMemo } = React;

// ─── PRODUCTS ────────────────────────────────────────────────────────────────

/**
 * @spec GA-060 — Products catalog (unified GA/CM/AM/OM role-param)
 * US-040 / US-041 / US-042. POS-owned catalog; admin is read-only except available toggle
 * (editable by all in-scope tiers). retail_price = READ-ONLY for all tiers incl GA.
 */
function SKUCatalog({ role = "GA" }) {
  if (!["GA","CM","AM","OM"].includes(role)) return _notAuthorised;

  const [searchQuery, setSearchQuery]       = useState("");
  const [primaryValue, setPrimaryValue]     = useState("");
  const [advancedValues, setAdvancedValues] = useState({ category: "", geo: { countries: [], areas: [], outlets: [] } });
  const [drawerFor, setDrawerFor]           = useState(null);
  const [salesPeriod, setSalesPeriod]       = useState("weekly");
  const [toast, setToast]                   = useState(null);
  const [page, setPage]                     = useState(1);
  const [pageSize, setPageSize]             = useState(20);
  // Optimistic availability state — keyed by outlet_menu_items id.
  const [availOverrides, setAvailOverrides] = useState({});
  // Operator remarks (Ops-module convention) — edit-own-only, keyed per product id.
  const [remarksByProduct, setRemarksByProduct] = useState({});
  const addProductRemark = (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) };
    setRemarksByProduct(m => ({ ...m, [id]: [...(m[id] || []), entry] }));
    _pushAudit({ action: "remark_added", entity_type: "product", entity_id: id, diff: { content } });
  };
  const editProductRemark = (id) => (remarkId, content) => {
    setRemarksByProduct(m => ({ ...m, [id]: (m[id] || []).map(r => r.id === remarkId ? { ...r, content } : r) }));
    _pushAudit({ action: "remark_edited", entity_type: "product", entity_id: id, diff: { id: remarkId, content } });
  };
  const deleteProductRemark = (id) => (remarkId) => {
    setRemarksByProduct(m => ({ ...m, [id]: (m[id] || []).filter(r => r.id !== remarkId) }));
    _pushAudit({ action: "remark_deleted", entity_type: "product", entity_id: id, diff: { id: remarkId } });
  };

  // ── Scope resolution ─────────────────────────────────────────────────────
  // GA = all SKUs; CM = own country; AM = own AREA (area-scoped, NOT country);
  // OM = own outlet. After GeoScope narrows, singleOutlet = scope is exactly 1 outlet.

  const geoScope = advancedValues.geo || { countries: [], areas: [], outlets: [] };

  // In-scope outlets for this role+geo combination.
  const inScopeOutlets = useMemo(() => {
    let outlets = SB3_OUTLETS;
    if (role === "CM") {
      outlets = outlets.filter(o => o.country_id === "PH"); // mock: CM is PH
    } else if (role === "AM") {
      outlets = outlets.filter(o => o.area_id === "ar_03"); // mock: AM is Metro Manila
    } else if (role === "OM") {
      outlets = outlets.filter(o => o.id === "SH-PH-001"); // mock: OM has one outlet
    }
    // Apply GeoScope narrowing (GA/CM/AM can narrow further via the filter).
    if (geoScope.outlets && geoScope.outlets.length > 0) {
      const ids = new Set(geoScope.outlets);
      outlets = outlets.filter(o => ids.has(o.id));
    } else if (geoScope.areas && geoScope.areas.length > 0) {
      const ids = new Set(geoScope.areas);
      outlets = outlets.filter(o => ids.has(o.area_id));
    } else if (geoScope.countries && geoScope.countries.length > 0) {
      const ids = new Set(geoScope.countries);
      outlets = outlets.filter(o => ids.has(o.country_id));
    }
    return outlets;
  }, [role, geoScope]);

  const singleOutlet = inScopeOutlets.length === 1 ? inScopeOutlets[0] : null;

  // Base SKUs filtered by role scope.
  const baseSKUs = useMemo(() => {
    if (role === "GA") return SB4_SKUS;
    if (role === "CM") return SB4_SKUS.filter(s => s.country_id === "PH");
    if (role === "AM") return SB4_SKUS.filter(s => s.country_id === "PH"); // AM area_03 is PH
    if (role === "OM") return SB4_SKUS.filter(s => s.country_id === "PH"); // OM outlet is PH
    return [];
  }, [role]);

  // For each SKU, compute availability across in-scope outlets.
  const enrichedSKUs = useMemo(() => {
    return baseSKUs.map(sku => {
      const omis = (SB4_OMI_BY_SKU[sku.id] || []).filter(omi =>
        inScopeOutlets.some(o => o.id === omi.outlet_id)
      );
      const total = omis.length;
      // Count available (applying optimistic overrides).
      const availCount = omis.filter(omi => {
        const override = availOverrides[omi.id];
        return override !== undefined ? override : omi.available;
      }).length;
      // retail_price is per-outlet (many values per product) → shown in the detail
      // drawer per-outlet rows only, never as a list column.
      return { ...sku, _omis: omis, _availCount: availCount, _total: total };
    });
  }, [baseSKUs, inScopeOutlets, availOverrides, singleOutlet]);

  // ── Filter + search ──────────────────────────────────────────────────────
  const rows = useMemo(() => {
    return enrichedSKUs
      .filter(s => !advancedValues.category || s.category_id === advancedValues.category)
      .filter(s => {
        if (!primaryValue) return true;
        if (primaryValue === "available") return s._availCount > 0;
        if (primaryValue === "unavailable") return s._availCount === 0;
        if (primaryValue === "partial") return s._availCount > 0 && s._availCount < s._total;
        return true;
      })
      .filter(s => {
        if (!searchQuery) return true;
        const q = searchQuery.toLowerCase();
        return s.name.toLowerCase().includes(q) || s.sku_code.toLowerCase().includes(q);
      });
  }, [enrichedSKUs, advancedValues.category, primaryValue, searchQuery]);

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

  // ── Scope description ────────────────────────────────────────────────────
  const scopeDescription = {
    GA: "All countries",
    CM: "Philippines",
    AM: "Metro Manila (Area)",
    OM: "This outlet",
  }[role];

  // ── Availability toggle (optimistic autosave) ────────────────────────────
  const handleToggle = (omiId, nextVal) => {
    setAvailOverrides(prev => ({ ...prev, [omiId]: nextVal }));
    setToast("Availability updated · syncs to consumer app");
    setTimeout(() => setToast(null), 2800);
  };

  // ── GeoScope filter data ─────────────────────────────────────────────────
  const geoCountries = SB2_COUNTRIES
    .filter(c => role === "GA" ? true : c.country_id === "PH")
    .map(c => ({ value: c.country_id, label: `${c.flag} ${c.country_name}` }));
  const geoAreas = SB3_AREAS
    .filter(a => role === "GA" ? true : a.country_id === "PH")
    .map(a => ({ value: a.id, label: a.name, country_id: a.country_id }));
  const geoOutlets = SB3_OUTLETS
    .filter(o => role === "OM" ? o.id === "SH-PH-001" : role === "CM" ? o.country_id === "PH" : role === "AM" ? o.area_id === "ar_03" : true)
    .map(o => ({ value: o.id, label: o.name, area_id: o.area_id }));

  const categoryOpts = [
    { value: "", label: "All categories" },
    ...SB4_CATEGORIES.map(c => ({ value: c.id, label: c.label })),
  ];
  const availOpts = [
    { value: "", label: "All" },
    { value: "available",   label: "Available" },
    { value: "unavailable", label: "Unavailable" },
    { value: "partial",     label: "Partial" },
  ];

  // ── Drawer ───────────────────────────────────────────────────────────────
  const selected = drawerFor ? enrichedSKUs.find(s => s.id === drawerFor) : null;

  // For the drawer Local Override section: one row per in-scope outlet.
  // retail_price is always read-only (POS-owned). available is editable.
  // NOTE: ListEditor does not support per-column read-only independently of
  // row.inherited (ro is row-level). Using canonical .list-editor CSS classes
  // directly to render the mixed-editability table instead of forking ListEditor.
  const drawerOutletRows = useMemo(() => {
    if (!selected) return [];
    return inScopeOutlets.map(outlet => {
      const omi = (selected._omis || []).find(o => o.outlet_id === outlet.id);
      const currentAvail = omi ? (availOverrides[omi.id] !== undefined ? availOverrides[omi.id] : omi.available) : false;
      return { outlet, omi, currentAvail };
    });
  }, [selected, inScopeOutlets, availOverrides]);

  // Single-outlet POS linkage fields for Linkage FormSection.
  const linkageOmi = singleOutlet && selected
    ? (selected._omis || []).find(o => o.outlet_id === singleOutlet.id)
    : null;

  return (
    <div className="page-inner">
      <PageHead
        title="Products"
        description={scopeDescription}
        actions={<ExportButton role={role} onClick={() => { setToast("Exporting products…"); setTimeout(() => setToast(null), 2400); }}/>}
      />

      <InlineAlert kind="info">
        Products are synced from POS. Catalog, price, and local price are read-only — update them in POS directly. Only availability can be toggled here.
      </InlineAlert>

      <FilterBar
        primaryFilter={{ key: "available", label: "Availability", options: availOpts }}
        primaryValue={primaryValue}
        onPrimaryChange={v => { setPrimaryValue(v); setPage(1); }}
        advancedFilters={[
          { key: "category", kind: "select", label: "Category", options: categoryOpts },
          {
            key:      "geo",
            kind:     "geoscope",
            label:    "Network scope",
            role:     role,
            countries: geoCountries,
            areas:     geoAreas,
            outlets:   geoOutlets,
          },
        ]}
        advancedValues={advancedValues}
        onAdvancedChange={(k, v) => { setAdvancedValues(prev => ({ ...prev, [k]: v })); setPage(1); }}
        onAdvancedReset={() => { setAdvancedValues({ category: "", geo: { countries: [], areas: [], outlets: [] } }); setPage(1); }}
        onReset={() => { setPrimaryValue(""); setAdvancedValues({ category: "", geo: { countries: [], areas: [], outlets: [] } }); setSearchQuery(""); setPage(1); }}
        search={searchQuery}
        onSearch={q => { setSearchQuery(q); setPage(1); }}
        searchPlaceholder="Search products…"
      />

      <Table
        emptyText="No products match these filters."
        columns={[
          {
            label: "Product", sortable: true, sortKey: "name",
            render: r => (
              <div style={{ display: "flex", flexDirection: "column", gap: 2, minWidth: 0 }}>
                <span style={{ ...T.primary() }}>{r.name}</span>
                <span style={{ ...T_MUTED, fontSize: 12 }}>{SB4_CAT_BY_ID[r.category_id]}</span>
              </div>
            ),
          },
          { label: "Price",       width: 120, sortable: true, sortKey: "price", render: r => <span style={T.amount}>{Object.values(r.price)[0] || "—"}</span> },
          {
            label: "Available", width: 160, sortable: true, sortKey: "_availCount",
            render: r => {
              if (singleOutlet) {
                const omi = (r._omis || []).find(o => o.outlet_id === singleOutlet.id);
                if (!omi) return <span style={{ ...T_MUTED }}>—</span>;
                const on = availOverrides[omi.id] !== undefined ? availOverrides[omi.id] : omi.available;
                return (
                  <span onClick={e => e.stopPropagation()}>
                    <Toggle on={on} onToggle={() => handleToggle(omi.id, !on)}/>
                  </span>
                );
              }
              // Multi-outlet: aggregate read-only text (terse — the "Available" column header carries the meaning).
              return <span style={{ ...T_MUTED }}>{`${r._availCount} of ${r._total}`}</span>;
            },
          },
          ...(role === "GA" ? [{
            label: "Country", width: 110, sortable: true, sortKey: "country_id",
            render: r => {
              const c = SB2_COUNTRY_BY_ID[r.country_id];
              return <span style={{ ...T_MUTED }}>{c ? `${c.flag} ${c.country_id}` : r.country_id}</span>;
            },
          }] : []),
          { label: "Last sync", width: 150, sortable: true, sortKey: "updated_at", render: r => _syncCell(r.updated_at, r.country_id, MOCK_NOW, true) },
        ]}
        rows={visibleRows}
        onRow={r => { setDrawerFor(r.id); setSalesPeriod("weekly"); }}
      />

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

      <DetailDrawer
        open={!!drawerFor}
        onClose={() => setDrawerFor(null)}
        title={selected ? selected.name : ""}
        subtitle={selected ? SB4_CAT_BY_ID[selected.category_id] : ""}
      >
        {selected && (
          <>
            {/* Section 1: General */}
            <FormSection title="General">
              <KeyValueGrid columns={2} items={[
                { label: "Name",         value: selected.name },
                { label: "Category",     value: SB4_CAT_BY_ID[selected.category_id] },
                { label: "POS item ID",  value: selected.pos_item_id || "—", mono: true },
                { label: "POS item UUID",value: selected.pos_item_uuid || "—", mono: true },
                { label: "Price",        value: Object.values(selected.price)[0] || "—" },
              ]}/>
            </FormSection>

            {/* Section 2: Linkage (country, last sync, outlet POS IDs at single-outlet scope) */}
            <FormSection title="Linkage">
              <KeyValueGrid columns={2} items={[
                ...(role === "GA" ? (() => {
                  const c = SB2_COUNTRY_BY_ID[selected.country_id];
                  return [{ label: "Country", value: c ? `${c.flag} ${c.country_name}` : selected.country_id }];
                })() : []),
                { label: "Last sync", value: _syncCell(selected.updated_at, selected.country_id, MOCK_NOW) },
                ...(linkageOmi ? [
                  { label: "POS store ID",        value: linkageOmi.pos_store_id || "—", mono: true },
                  { label: "POS parent store ID", value: linkageOmi.pos_parent_store_id || "—", mono: true },
                ] : singleOutlet ? [
                  { label: "POS store ID",        value: singleOutlet.pos_store_id || "—", mono: true },
                  { label: "POS parent store ID", value: singleOutlet.pos_parent_store_id || "—", mono: true },
                ] : [
                  { label: "POS store IDs", value: "Varies by outlet — select one outlet to see details" },
                ]),
              ]}/>
            </FormSection>

            {/* Section: Pricing (read-only POS) — catalog-level retail_price (POS-synced;
                read-only for all tiers incl GA, per 03-ui Table 2 Local→Pricing). */}
            <FormSection title="Pricing (read-only POS)">
              <KeyValueGrid columns={2} items={[
                { label: "Retail price", value: <span style={T.amount}>{Object.values(selected.price)[0] || "—"}</span> },
              ]}/>
            </FormSection>

            {/* Section 3: Local override — one row per in-scope outlet.
                NOTE: ListEditor does not support per-column read-only independently
                of row.inherited (ro is row-level, not field-level). The available
                column must be editable while retail_price is always read-only.
                Using .list-editor canonical CSS classes directly to render the
                mixed-editability table without forking ListEditor. */}
            <FormSection title="Local override">
              <div className="list-editor">
                <div className="list-editor-table">
                  <div className="le-head">
                    <div className="le-row-fields" style={{ gridTemplateColumns: "1fr 100px 140px" }}>
                      <div className="le-col-label">Outlet</div>
                      <div className="le-col-label">Available</div>
                      <div className="le-col-label">Retail price (POS)</div>
                    </div>
                  </div>
                  <div className="list-editor-rows">
                    {drawerOutletRows.length === 0 && (
                      <div className="list-editor-empty">No outlets in scope.</div>
                    )}
                    {drawerOutletRows.map(({ outlet, omi, currentAvail }) => (
                      <div key={outlet.id} className="le-row">
                        <div className="le-row-fields" style={{ gridTemplateColumns: "1fr 100px 140px" }}>
                          <div className="le-cell">
                            <span style={{ ...T_MUTED, fontSize: 13 }}>{outlet.name}</span>
                          </div>
                          <div className="le-cell">
                            {omi ? (
                              <Toggle
                                on={currentAvail}
                                onToggle={() => handleToggle(omi.id, !currentAvail)}
                              />
                            ) : (
                              <span style={{ ...T_MUTED }}>—</span>
                            )}
                          </div>
                          <div className="le-cell">
                            {/* retail_price read-only (POS). Bold when it differs from the base price. */}
                            {(() => {
                              const differs = omi && omi.retail_price && omi.retail_price !== Object.values(selected.price)[0];
                              return (
                                <span style={differs ? { ...T.amount } : { ...T_MUTED, fontFeatureSettings: '"tnum"' }}>
                                  {omi ? omi.retail_price : "—"}
                                </span>
                              );
                            })()}
                          </div>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            </FormSection>

            {/* Section 4: Units sold — read-only derived analytics (03-ui §2 Sales). */}
            <FormSection title="Units sold">
              <ChartCard
                kind="bar"
                data={_skuSales(selected.id, salesPeriod)}
                height={180}
                actions={
                  <Select
                    value={salesPeriod}
                    onChange={setSalesPeriod}
                  >
                    <option value="daily">Daily</option>
                    <option value="weekly">Weekly</option>
                    <option value="monthly">Monthly</option>
                    <option value="yearly">Yearly</option>
                  </Select>
                }
              />
            </FormSection>

            <RemarksSection entityType="product" entityId={selected.id} remarks={remarksByProduct[selected.id] || []} onAdd={addProductRemark(selected.id)} currentUser="you" onEdit={editProductRemark(selected.id)} onDelete={deleteProductRemark(selected.id)} role={role}/>
          </>
        )}
      </DetailDrawer>

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

// PartnerRewards and CMPartnerReward relocated to page-rewards.jsx (Rewards module consolidation).

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