/* page-loyalty.jsx — Loyalty points ledger. Ops module, all roles (GA/CM/AM/OM). @spec US-055 */
(function () {
  const { useState, useMemo } = React;

  // ── Loyalty tier derivation (cumulative earned, never demotes, ALL-scope tiers) ──
  // Tiers from CUSTOM_OPTIONS: Bronze 0 / Silver 500 / Gold 2000 (scope:"ALL").
  // Uses only txn_type "earned" (positive earned txns); ignores PH per-country overrides per spec.
  const TIER_THRESHOLDS = [
    { label: "Gold",   min: 2000 },
    { label: "Silver", min: 500 },
    { label: "Bronze", min: 0 },
  ];
  const _deriveTier = (customerId, ledger) => {
    const cumEarned = ledger
      .filter(r => r.customer_id === customerId && r.txn_type === "earned" && r.points > 0)
      .reduce((sum, r) => sum + r.points, 0);
    const tier = TIER_THRESHOLDS.find(t => cumEarned >= t.min);
    return tier ? tier.label : "Bronze";
  };

  // ── Points sign rendering — debit rows (spent / refund) show berry ink ──
  const _pointsStyle = (points) =>
    points < 0
      ? { color: "var(--berry)", fontWeight: 600, fontFamily: "var(--font-ui)" }
      : { color: "var(--forest)", fontWeight: 600, fontFamily: "var(--font-ui)" };

  const _fmtPoints = (points) =>
    points > 0 ? `+${points.toLocaleString()}` : points.toLocaleString();

  // ── txn_type → StatusPill: kind="loyalty" (tone map in STATUS_TONES), status=raw txn_type ──
  const TXN_LABEL = {
    earned:     "Earned",
    spent:      "Spent",
    refund:     "Refund",
    adjustment: "Adjustment",
  };

  // ── txn_type filter options ──
  const TXN_FILTER_OPTS = [
    { value: "", label: "All types" },
    { value: "earned",     label: "Earned" },
    { value: "spent",      label: "Spent" },
    { value: "refund",     label: "Refund" },
    { value: "adjustment", label: "Adjustment" },
  ];

  // ── Customer helper ──
  const _custName = (id) => {
    const c = typeof CM_CUSTOMER_BY_ID !== "undefined" ? CM_CUSTOMER_BY_ID[id] : null;
    return c ? c.name : id;
  };
  const _custEmail = (id) => {
    const c = typeof CM_CUSTOMER_BY_ID !== "undefined" ? CM_CUSTOMER_BY_ID[id] : null;
    return c ? c.email : "—";
  };
  const _custPhone = (id) => {
    const c = typeof CM_CUSTOMER_BY_ID !== "undefined" ? CM_CUSTOMER_BY_ID[id] : null;
    return c ? c.phone : "—";
  };

  // ── Outlet label helper ──
  const _outletLabel = (id) => {
    const o = typeof SB3_OUTLET_BY_ID !== "undefined" ? SB3_OUTLET_BY_ID[id] : null;
    return o ? o.location : (id || "—");
  };

  // Remarks sub-block — keeps _useRemarks (a hook) at a stable top level, not inside the drawer JSX.
  function LoyaltyTxnRemarks({ txnId, seed }) {
    const [remarks, addRemark, editRemark, deleteRemark] = _useRemarks("loyalty_txn", txnId, seed || []);
    return <RemarksSection entityType="loyalty_txn" entityId={txnId} remarks={remarks} onAdd={addRemark} currentUser="you" onEdit={editRemark} onDelete={deleteRemark}/>;
  }

  /** @spec US-055 */
  function Loyalty({ role, scope }) {
    const [search,    setSearch]    = useState("");
    const [txnFilter, setTxnFilter] = useState("");
    // Inline DateTimeFilter state — null shape = default (Today · Full day).
    // Single source of truth for BOTH the date/range slice and the time-window slice,
    // shared by the inline FilterBar control and the advanced-drawer entry (key "dtf").
    const [dtf,       setDtf]       = useState(null);
    // GeoScope state (GA/CM/AM only) — canonical {countries,areas,outlets} (matches GeoScopeFilter)
    const [geo,       setGeo]       = useState({ countries: [], areas: [], outlets: [] });
    const [page,      setPage]      = useState(1);
    const [pageSize,  setPageSize]  = useState(25);
    const [selected,  setSelected]  = useState(null);
    const [drawerOpen, setDrawerOpen] = useState(false);

    const ledger = typeof LOYALTY_LEDGER !== "undefined" ? LOYALTY_LEDGER : [];

    // ── Filter scope — Loyalty is MULTI-country, so it uses the RAW header `scope`
    // (unlike Wallet, which pins SG). todOptions = scope-driven time_of_day presets;
    // filterTz = baseline FILTER tz from scope. Row DISPLAY tz stays record-local
    // (`r.country_id` in the column renders); ROW-MATCH uses record-local tz too. ──
    const todOptions = useMemo(
      () => _scopePresets(scope, typeof CUSTOM_OPTIONS !== "undefined" ? CUSTOM_OPTIONS : []),
      [scope]
    );
    const filterTz = _scopeTz(scope);

    // ── Memoised filter → scoped rows ──
    const rows = useMemo(() => {
      let r = [...ledger];

      // Scope by role
      if (role === "CM") {
        // CM scoped to own country — mock: PH
        r = r.filter(x => x.country_id === "PH");
      } else if (role === "AM") {
        // AM scoped to own area outlets — mock: ar_03 outlets
        const areaOutlets = typeof SB3_OUTLETS !== "undefined"
          ? SB3_OUTLETS.filter(o => o.area_id === (typeof AM_AREA_ID !== "undefined" ? AM_AREA_ID : "ar_03")).map(o => o.id)
          : ["SH-PH-001", "SH-PH-002", "SH-PH-004"];
        r = r.filter(x => areaOutlets.includes(x.outlet_id));
      } else if (role === "OM") {
        // OM scoped to own outlet
        const omOutlet = typeof OM_OUTLET_ID !== "undefined" ? OM_OUTLET_ID : "SH-PH-001";
        r = r.filter(x => x.outlet_id === omOutlet);
      }
      // GA: no scope filter — global

      // GeoScope (GA/CM/AM only) — applied after role scope
      if (role !== "OM") {
        if (geo.countries && geo.countries.length)
          r = r.filter(x => geo.countries.includes(x.country_id));
        if (geo.areas && geo.areas.length) {
          const scopedOutlets = typeof SB3_OUTLETS !== "undefined"
            ? SB3_OUTLETS.filter(o => geo.areas.includes(o.area_id)).map(o => o.id)
            : [];
          if (scopedOutlets.length) r = r.filter(x => scopedOutlets.includes(x.outlet_id));
        }
        if (geo.outlets && geo.outlets.length)
          r = r.filter(x => geo.outlets.includes(x.outlet_id));
      }

      // txn_type filter
      if (txnFilter) r = r.filter(x => x.txn_type === txnFilter);

      // Date + time-of-day filter — shared predicate, evaluated in the record-local
      // tz (x.country_id, falling back to SG). Single source of truth for date/range
      // AND time-window slicing; runs on the source array BEFORE pagination slice.
      r = r.filter(x => _dtfMatch(dtf, x.created_at, x.country_id || "SG"));

      // Search (customer id/name)
      if (search) {
        const q = search.toLowerCase();
        r = r.filter(x =>
          x.id.toLowerCase().includes(q) ||
          x.customer_id.toLowerCase().includes(q) ||
          _custName(x.customer_id).toLowerCase().includes(q)
        );
      }

      return r;
    }, [ledger, role, txnFilter, dtf, search, geo]);

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

    // ── Columns — GA/CM/AM full; OM reduced ──
    const columns = role === "OM"
      ? [
          { key: "id",          label: "ID",          sortable: true,  render: r => <span style={{ ...T.primary() }}>{r.id}</span> },
          { key: "customer_id", label: "Customer",    sortable: true,  sortKey: "customer_id",
            render: r => <span style={{ ...T.primary() }}>{_custName(r.customer_id)}</span> },
          { key: "txn_type",    label: "Type",        sortable: true,  sortKey: "txn_type",
            render: r => <StatusPill status={r.txn_type} kind="loyalty"/> },
          { key: "points",      label: "Points",      sortable: true,
            render: r => <span style={_pointsStyle(r.points)}>{_fmtPoints(r.points)}</span> },
          { key: "created_at",  label: "Date",        sortable: true,
            render: r => <span style={{ ...T.muted, whiteSpace: "nowrap" }}>{_fmtDateTime(r.created_at, r.country_id)}</span> },
        ]
      : [
          { key: "id",                label: "ID",              sortable: true,  render: r => <span style={{ ...T.primary() }}>{r.id}</span> },
          { key: "customer_id",       label: "Customer",        sortable: true,  sortKey: "customer_id",
            render: r => (
              <span>
                <span style={{ ...T.primary() }}>{_custName(r.customer_id)}</span>
                <span style={{ ...T.muted, marginLeft: 6, fontSize: 11 }}>{_deriveTier(r.customer_id, ledger)}</span>
              </span>
            )
          },
          { key: "txn_type",          label: "Type",            sortable: true,  sortKey: "txn_type",
            render: r => <StatusPill status={r.txn_type} kind="loyalty"/> },
          { key: "points",            label: "Points",          sortable: true,
            render: r => <span style={_pointsStyle(r.points)}>{_fmtPoints(r.points)}</span> },
          { key: "transaction_amount",label: "Amount",          sortable: true,  sortKey: "transaction_amount",
            render: r => r.transaction_amount != null
              ? <span style={{ ...T.muted, whiteSpace: "nowrap" }}>₱ {r.transaction_amount.toLocaleString("en-PH", { minimumFractionDigits: 2 })}</span>
              : <span style={T.muted}>—</span> },
          { key: "order_id",          label: "Order",           sortable: true,  sortKey: "order_id",
            render: r => r.order_id
              ? <a className="ghost-link" onClick={e => { e.stopPropagation(); _go("ga-order-detail", { id: r.order_id }); }} style={{ cursor: "pointer" }}>{r.order_id}</a>
              : <span style={T.muted}>—</span> },
          { key: "created_at",        label: "Date",            sortable: true,
            render: r => <span style={{ ...T.muted, whiteSpace: "nowrap" }}>{_fmtDateTime(r.created_at, r.country_id)}</span> },
        ];

    // ── Descriptions per role ──
    const descriptions = {
      GA: "Customer points ledger.",
      CM: "Your country's points.",
      AM: "Your area's points.",
      OM: "Your outlet's points.",
    };

    // ── Drawer selected row ──
    const txn = selected;

    // ── FilterBar config (canonical v2.2 API: primaryValue / advancedValues) ──
    const primaryFilter = {
      key: "txn_type",
      label: "Type",
      options: TXN_FILTER_OPTS,
    };

    // GeoScope advanced filter — must carry role + the country/area/outlet option data
    // (FilterDrawer kind:"geoscope" passes f.countries/f.areas/f.outlets straight to GeoScopeFilter).
    const advancedFilters = [
      ...(role !== "OM" ? [{
        key: "geo", kind: "geoscope", label: "Network scope", role,
        countries: typeof SB2_COUNTRIES !== "undefined"
          ? SB2_COUNTRIES.map(c => ({ value: c.country_id, label: `${c.flag} ${c.country_name}` })) : [],
        areas: typeof SB3_AREAS !== "undefined"
          ? SB3_AREAS.map(a => ({ value: a.id, label: a.name, country_id: a.country_id })) : [],
        outlets: typeof SB3_OUTLETS !== "undefined"
          ? SB3_OUTLETS.map(o => ({ value: o.id, label: o.location, area_id: o.area_id })) : [],
      }] : []),
      // Date + time-of-day is ONE DateTimeFilter (single source of truth, the `dtf` state).
      // Surfaced BOTH inline (dateTimeFilter prop) AND here in the advanced drawer; both edit
      // the same `dtf` state, so a change in either place updates the other.
      { key: "dtf", kind: "datetime", label: "Date & time", country: filterTz, todOptions },
    ];

    const advancedValues = {
      ...(role !== "OM" ? { geo } : {}),
      dtf,
    };

    const onAdvancedChange = (k, v) => {
      if (k === "geo")      { setGeo(v); setPage(1); }
      else if (k === "dtf") { setDtf(v); setPage(1); }
    };

    const onAdvancedReset = () => {
      setGeo({ countries: [], areas: [], outlets: [] });
      setDtf(null);
      setPage(1);
    };

    const onReset = () => {
      setSearch("");
      setTxnFilter("");
      setDtf(null);
      setGeo({ countries: [], areas: [], outlets: [] });
      setPage(1);
    };

    return (
      <div className="page-inner">
        <PageHead
          title="Loyalty"
          description={descriptions[role] || "Points ledger."}
          actions={<ExportButton role={role} onClick={() => {}}/>}
        />

        <FilterBar
          search={search}
          onSearch={q => { setSearch(q); setPage(1); }}
          searchPlaceholder="Search customers or IDs…"
          primaryFilter={primaryFilter}
          primaryValue={txnFilter}
          onPrimaryChange={v => { setTxnFilter(v); setPage(1); }}
          dateTimeFilter={{
            value: dtf,
            onChange: v => { setDtf(v); setPage(1); },
            country: filterTz,
            todOptions: todOptions,
          }}
          advancedFilters={advancedFilters}
          advancedValues={advancedValues}
          onAdvancedChange={onAdvancedChange}
          onAdvancedReset={onAdvancedReset}
          onReset={onReset}
        />

        <Table
          dense
          columns={columns}
          rows={visibleRows}
          emptyText="No transactions match your filters."
          onRow={r => {
            setSelected(r);
            setDrawerOpen(true);
          }}
        />

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

        {txn && (
          <DetailDrawer
            open={drawerOpen}
            onClose={() => setDrawerOpen(false)}
            title={txn.id}
            subtitle={TXN_LABEL[txn.txn_type] || txn.txn_type}
          >
            <FormSection title="General">
              <KeyValueGrid items={[
                { label: "ID",     value: txn.id },
                { label: "Type",   value: <StatusPill status={txn.txn_type} kind="loyalty"/> },
                { label: "Points", value: <span style={_pointsStyle(txn.points)}>{_fmtPoints(txn.points)}</span> },
                { label: "Amount", value: txn.transaction_amount != null
                    ? `₱ ${txn.transaction_amount.toLocaleString("en-PH", { minimumFractionDigits: 2 })}`
                    : "—" },
                { label: "Date",   value: _fmtDateTime(txn.created_at, txn.country_id) },
              ]}/>
            </FormSection>

            <FormSection title="Customer">
              <KeyValueGrid items={[
                { label: "Customer ID",   value: txn.customer_id },
                { label: "Name",          value: _custName(txn.customer_id) },
                { label: "Email",         value: _emailLink(_custEmail(txn.customer_id)) },
                { label: "Phone",         value: _phoneLink(_custPhone(txn.customer_id)) },
                { label: "Loyalty tier",  value: _deriveTier(txn.customer_id, ledger) },
              ]}/>
            </FormSection>

            <FormSection title="Context">
              <KeyValueGrid items={[
                { label: "Order",   value: txn.order_id
                    ? <a className="ghost-link" onClick={() => _go("ga-order-detail", { id: txn.order_id })} style={{ cursor: "pointer" }}>{txn.order_id}</a>
                    : "—" },
                { label: "Country", value: _countryLabel(txn.country_id) },
                { label: "Outlet",  value: _outletLabel(txn.outlet_id) },
              ]}/>
            </FormSection>

            <LoyaltyTxnRemarks key={txn.id} txnId={txn.id} seed={txn.loyalty_remarks}/>
          </DetailDrawer>
        )}
      </div>
    );
  }

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