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

// CM/AM/OM land on their OWN franchise (mock acting-user → franchise resolution, mirrors CountryDetail own-scope).
const _OWN_FRANCHISE_BY_ROLE = { CM: "fr_02", AM: "fr_02", OM: "fr_02" };
// Reverse-lookup the Country Manager for a franchise (Users are the source — never a stored cm_user_id; convention 11/34).
const _frCm = (frId) => SB3_USERS.find(u => u.franchise_id === frId && u.role_type === "country_manager") || null;

/**
 * @spec GA-030 — Franchisees list
 * US-010/011/013. GA-only full table (non-GA short-circuit to own Detail). GeoScope + status filters,
 * row click navigates to full-page Detail (Network convention §14). No kebab.
 */
function FranchiseesList({ role = "GA" }) {
  const [status, setStatus] = useState("");
  const [searchQuery, setSearchQuery] = useState("");
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(20);
  if (role !== "GA") return _notAuthorised;

  // Enrich rows with derived sort keys (outlet count + CM name) — derived columns need a field for sortKey.
  const rows = useMemo(() => SB3_FRANCHISEES
    .filter(f => !status || f.status === status)
    .filter(f => {
      if (!searchQuery.trim()) return true;
      const q = searchQuery.toLowerCase();
      return (f.display_name || "").toLowerCase().includes(q)
          || (f.legal_name || "").toLowerCase().includes(q);
    })
    .map(f => {
      const cm = _frCm(f.id);
      return { ...f, _outlet_count: SB3_OUTLETS.filter(o => o.franchise_id === f.id).length, _cm_name: cm ? cm.full_name : "" };
    }), [status, searchQuery]);

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

  return (
    <div className="page-inner">
      <PageHead title="Franchisees" description="Franchise partners across the network."
        actions={
          <div className="row" style={{ gap: 8 }}>
            <Btn variant="primary" onClick={() => _go("ga-franchisee-edit")}>New franchisee</Btn>
          </div>
        }/>

      <FilterBar
        primaryFilter={{ key: "status", label: "Status", options: [
          { value: "", label: "All statuses" },
          ...["draft","active","inactive"].map(s => ({ value: s, label: s })),
        ]}}
        primaryValue={status}
        onPrimaryChange={setStatus}
        onReset={() => { setStatus(""); setSearchQuery(""); }}
        search={searchQuery}
        onSearch={setSearchQuery}
        searchPlaceholder="Search franchisees…"
      />

      <Table
        emptyText="No franchisees match these filters."
        columns={[
          { label: "Country",   width: 140, sortable: true, sortKey: "country_id", render: r => <span style={{ ...T.primary(1) }}>{_countryLabel(r.country_id)}</span> },
          { label: "Franchisee", sortable: true, sortKey: "display_name", render: r => (
            <div style={{ display: "flex", flexDirection: "column", gap: 2, minWidth: 0 }}>
              <span style={{ ...T.primary() }}>{r.display_name}</span>
              <span style={{ ...T_MUTED, fontSize: 12 }}>{r.legal_name}</span>
            </div>
          ) },
          { label: "Country Mgr",  width: 160, sortable: true, sortKey: "_cm_name", render: r => r._cm_name
              ? <span style={{ ...T_MUTED }}>{r._cm_name}</span>
              : <span style={{ ...T_MUTED, fontStyle: "italic" }}>unassigned</span> },
          { label: "Outlets",      width: 90,  sortable: true, sortKey: "_outlet_count", render: r => <span style={{ ...T_MUTED, fontFeatureSettings: '"tnum"' }}>{r._outlet_count}</span> },
          { label: "Status",       width: 130, sortable: true, sortKey: "status", render: r => <StatusPill status={r.status} kind="franchisee"/> },
          { label: "1st order",    width: 90,  sortable: true, sortKey: "first_order_at", render: r => r.first_order_at
              ? <span title="First order processed" aria-label="First order processed">{Ic.check(16)}</span>
              : <span title="No order yet" style={{ color: "var(--ink-2)" }}>—</span> },
          { label: "Last activity",width: 140, sortable: true, sortKey: "last_activity", render: r => <span style={{ ...T_MUTED }}>{r.last_activity}</span> },
        ]}
        rows={visibleRows}
        onRow={r => _go("ga-franchisee-detail", { id: r.id })}
      />

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

/**
 * @spec GA-031 — Franchisee create / edit (GA-only, one inline page)
 * US-010/012. Sections: Identity · Assignment (CM via ManagerSection edit) · Contact.
 * Cancel/Save at top AND bottom. Move-country lives in Settings, not here.
 */
function FranchiseeEdit({ role = "GA" }) {
  const payloadId = typeof window !== "undefined" && window.__sixhands_route_payload
    ? window.__sixhands_route_payload.id : null;
  const existing = payloadId ? (SB3_FRANCHISEE_BY_ID[payloadId] || null) : null;
  const isNew = !existing;

  const [form, setForm] = useState(existing ? { ...existing } : {
    id: "fr_new", country_id: "", legal_name: "", display_name: "",
    status: "draft", contact_email: "", contact_phone: "",
    merchant_account_ref: null,
  });
  const [cmUserId, setCmUserId] = useState(existing ? (( _frCm(existing.id) || {}).id || null) : null);
  const [errors, setErrors] = useState({});
  const [savedToast, setSavedToast] = useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  if (role !== "GA") return _notAuthorised;

  const assignedCM = cmUserId ? (SB3_USERS.find(u => u.id === cmUserId) || null) : null;
  const eligibleCMs = SB3_USERS.filter(u => u.role_type === "country_manager" && (!form.country_id || u.country_id === form.country_id));

  const validate = () => {
    const e = {};
    if (!form.legal_name) e.legal_name = "Legal entity name is required.";
    if (!form.display_name) e.display_name = "Display name is required.";
    if (!form.country_id) e.country_id = "Country is required.";
    if (!form.contact_email) e.contact_email = "Contact email is required.";
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  const save = () => {
    if (!validate()) return;
    setSavedToast(true);
    // Land on the franchisee's Detail after saving. (Prototype: a new franchisee has no real
    // record, so route to a representative existing one for the demo; edits return to their own.)
    setTimeout(() => _go("ga-franchisee-detail", { id: isNew ? "fr_02" : form.id }), 650);
  };

  const saveLabel = isNew ? "Create franchisee" : "Save changes";

  return (
    <div className="page-inner">
      <PageHead title={isNew ? "New franchisee" : `${form.display_name} · Franchisee`}
        back={{ label: "Franchisees", onClick: () => _go("ga-franchisees") }}
        actions={
          <div className="row" style={{ gap: 8 }}>
            <Btn variant="secondary" onClick={() => _go("ga-franchisees")}>Cancel</Btn>
            <Btn variant="primary" icon={<span style={{ color: "var(--lime)" }}>{Ic.check(16)}</span>} onClick={save}>{saveLabel}</Btn>
          </div>
        }/>

      <FormSection title="Identity" description="Legal + public names. Country is immutable once the franchisee is active.">
        <div className="row" style={{ gap: 16 }}>
          <div className="grow">
            <Field label="Display name" required error={errors.display_name} hint="Shown to customers in app + receipts.">
              <Input value={form.display_name} onChange={v => set("display_name", v)} placeholder="e.g. FoodWave PH"/>
            </Field>
          </div>
          <div className="grow">
            <Field label="Legal entity name" required error={errors.legal_name}>
              <Input value={form.legal_name} onChange={v => set("legal_name", v)} placeholder="e.g. FoodWave Holdings Inc."/>
            </Field>
          </div>
        </div>
        <Field label="Country" required error={errors.country_id} hint={!isNew && form.status === "active" ? "Immutable while active." : undefined}>
          <Select value={form.country_id} onChange={v => set("country_id", v)} placeholder="Choose country">
            {SB2_COUNTRIES.map(c =>
              <option key={c.country_id} value={c.country_id} disabled={!isNew && form.status === "active" && c.country_id !== form.country_id}>
                {c.flag} {c.country_name}
              </option>
            )}
          </Select>
        </Field>
      </FormSection>

      <FormSection title="Assignment" description="The Country Manager is the senior in-country contact for this franchisee.">
        {assignedCM && (
          <div style={{ marginBottom: 12 }}>
            <ManagerSection manager={assignedCM} mode="read" roleType="Country Manager"/>
          </div>
        )}
        <ManagerSection
          mode={isNew ? "create" : "edit"}
          roleType="Country Manager"
          eligibleUsers={eligibleCMs.map(u => ({ id: u.id, full_name: u.full_name, email: u.email }))}
          onAssignExisting={setCmUserId}
          onCreateInline={isNew ? ({ name, email }) => {} : undefined}
        />
      </FormSection>

      <FormSection title="Contact" description="How HQ reaches this franchisee. Used for onboarding + billing notifications.">
        <div className="row" style={{ gap: 16 }}>
          <div className="grow">
            <Field label="Contact email" required error={errors.contact_email}>
              <Input type="email" value={form.contact_email} onChange={v => set("contact_email", v)} placeholder="ops@partner.com" autoComplete="email"/>
            </Field>
          </div>
          <div className="grow">
            <Field label="Contact phone">
              <Input type="tel" value={form.contact_phone} onChange={v => set("contact_phone", v)} placeholder="+63 917 …" autoComplete="tel"/>
            </Field>
          </div>
        </div>
      </FormSection>

      <FormSection title="Status" description="Draft franchisees are onboarding (never live). Active franchisees can transact; inactive franchisees are paused. Once active, a franchisee toggles active ⇄ inactive and cannot return to draft.">
        <div style={{ width: 240 }}>
          <Field label="Status">
            <Select value={form.status} onChange={v => set("status", v)}>
              {form.status === "draft" && <option value="draft">Draft</option>}
              <option value="active">Active</option>
              <option value="inactive">Inactive</option>
            </Select>
          </Field>
        </div>
      </FormSection>

      {/* Bottom Save/Cancel row (Cancel/Save at both top and bottom). */}
      <div className="row jc-e" style={{ gap: 8 }}>
        <Btn variant="secondary" onClick={() => _go("ga-franchisees")}>Cancel</Btn>
        <Btn variant="primary" icon={<span style={{ color: "var(--lime)" }}>{Ic.check(16)}</span>} onClick={save}>{saveLabel}</Btn>
      </div>

      <Toast message="Saved · audit_logs updated" show={savedToast}/>
    </div>
  );
}

/**
 * @spec GA-032 — Franchisee detail (full-page, 4 tabs role-branched)
 * US-011/013. Tabs: Overview · Onboarding · Settings(GA-only).
 * GA = full + Edit + Settings. CM/AM/OM = read-only "My Franchise" (Overview · Onboarding), no Edit/Settings.
 */
function FranchiseeDetail({ role = "GA" }) {
  const isGA = role === "GA";
  const payloadId = typeof window !== "undefined" && window.__sixhands_route_payload ? window.__sixhands_route_payload.id : null;
  // GA resolves from payload; non-GA resolve their OWN franchise (mock acting-user scope).
  const effectiveId = payloadId || (isGA ? "fr_02" : (_OWN_FRANCHISE_BY_ROLE[role] || "fr_02"));
  const fr = SB3_FRANCHISEE_BY_ID[effectiveId] || SB3_FRANCHISEE_BY_ID.fr_02;

  const canEdit = isGA;
  const canSeeAudit = isGA;

  const [tab, setTab] = useState("overview");
  const [confirm, setConfirm] = useState(null);          // { variant: 'suspend'|'terminate'|'move' }
  const [confirmReason, setConfirmReason] = useState("");
  const [moveTargetFr, setMoveTargetFr] = useState("");
  const [actionToast, setActionToast] = useState(null);

  const outletsOfFr = SB3_OUTLETS.filter(o => o.franchise_id === fr.id);
  const usersOfFr   = SB3_USERS.filter(u => u.franchise_id === fr.id);
  const cmUser      = _frCm(fr.id);
  const intStatus   = _getIntegrationStatus(fr.country_id);
  const roleLc      = role.toLowerCase();
  const filterPayload = { filter: { franchise_id: fr.id, label: fr.display_name } };

  // Role-gated tabs: GA = Overview · Onboarding · Settings; non-GA = Overview · Onboarding.
  const detailTabs = [
    { id: "overview",   label: "Overview" },
    { id: "onboarding", label: "Onboarding" },
    ...(isGA ? [{ id: "settings", label: "Settings" }] : []),
  ];

  // ── ONBOARDING milestones (5; collapse to summary at 100%). ──
  const payStatus = intStatus.payment_gateway_status; // connected | pending | not_started
  const hasCm = !!cmUser;
  const gaManaged = !hasCm && fr.status === "active";
  const hasOutlet = outletsOfFr.length > 0;
  const milestones = [
    { id: "contract", label: "Contract signed + country assigned", status: "done", detail: `${fr.legal_name} · ${_countryLabel(fr.country_id)}` },
    { id: "cm",       label: "Country Manager assigned", status: (hasCm || gaManaged) ? "done" : "active",
      detail: hasCm ? `${cmUser.full_name} · ${cmUser.email}` : (gaManaged ? "Managed directly by HQ." : "Assign a CM via Edit.") },
    { id: "payment",  label: "Payment gateway provisioned + connected",
      status: payStatus === "connected" ? "done" : (payStatus === "pending" ? "active" : "blocked"),
      detail: payStatus === "connected" ? "Gateway connected." : (payStatus === "pending" ? "Provisioning in progress." : "Pending DX provisioning — keys never exposed in UI.") },
    { id: "outlet",   label: "First outlet created", status: hasOutlet ? "done" : "active", detail: hasOutlet ? `${outletsOfFr.length} outlet(s) live` : "No outlets yet." },
    { id: "order",    label: "First order completed", status: fr.first_order_at ? "done" : "active", detail: fr.first_order_at ? `First order ${fr.first_order_at}` : "Awaiting first order." },
  ];
  const doneCount = milestones.filter(s => s.status === "done").length;

  const overview = (
    <>
      <FormSection title="Identity">
        <KeyValueGrid columns={2} items={[
          { label: "Display name",     value: fr.display_name },
          { label: "Legal name",       value: fr.legal_name },
          { label: "Country",          value: _countryLabel(fr.country_id) },
          { label: "Country Manager",  value: cmUser ? <span style={{ display: "inline-flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>{cmUser.full_name}{_emailLink(cmUser.email)}</span> : "— unassigned —" },
        ]}/>
      </FormSection>
      <FormSection title="Contact">
        <KeyValueGrid columns={2} items={[
          { label: "Contact email", value: _emailLink(fr.contact_email) },
          { label: "Contact phone", value: _phoneLink(fr.contact_phone) },
        ]}/>
      </FormSection>
      <FormSection title="Related" description="Open the records that belong to this franchisee.">
        <div className="stat-panel stat-panel-snapshot">
          <div className="stat-panel-grid">
            <StatCard label="Outlets" value={outletsOfFr.length} sub="View outlets for this franchisee"
              icon={<LIcon name="Store" size={16}/>}
              onClick={() => _go(`${roleLc}-outlets`, filterPayload)}/>
            <StatCard label="Users" value={usersOfFr.length} sub="View users for this franchisee"
              icon={<LIcon name="Users" size={16}/>}
              onClick={() => _go(`${roleLc}-users`, filterPayload)}/>
            {canSeeAudit && (
              <StatCard label="Audit Log" value="View" sub="Audit entries for this franchisee"
                icon={<LIcon name="ScrollText" size={16}/>}
                onClick={() => _go("ga-audit", filterPayload)}/>
            )}
          </div>
        </div>
      </FormSection>
    </>
  );

  const onboarding = (
    <FormSection title="Onboarding" description="Computed from franchisee state.">
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div className="row ai-c" style={{ gap: 12 }}>
          <ProgressBar value={doneCount} max={milestones.length} thresholds={{ warn: 2, good: milestones.length }} showLabel/>
          <span style={{ ...T_MUTED }}>
            {doneCount === milestones.length ? "Onboarding complete" : `${doneCount} of ${milestones.length} milestones`}
          </span>
        </div>
        <ChecklistProgress steps={milestones}/>
        {milestones.some(s => s.status === "blocked") && canSeeAudit && (
          <InlineAlert kind="error" action={<Btn variant="ghost" size="sm" onClick={() => _go("ga-audit", filterPayload)}>View audit</Btn>}>
            Onboarding blocked — see audit_logs for the failing step.
          </InlineAlert>
        )}
      </div>
    </FormSection>
  );

  return (
    <div className="page-inner">
      <PageHead
        back={isGA ? { label: "Franchisees", onClick: () => _go("ga-franchisees") } : undefined}
        title={isGA ? `${fr.display_name} · Franchisee` : "My Franchise"}
        status={<StatusPill status={fr.status} kind="franchisee"/>}
        actions={canEdit ? <Btn variant="primary" onClick={() => _go("ga-franchisee-edit", { id: fr.id })}>Edit</Btn> : undefined}
      />
      <Tabs tabs={detailTabs} active={tab} onChange={setTab}/>

      {tab === "overview"   && overview}
      {tab === "onboarding" && onboarding}
      {tab === "settings" && isGA && (
        <>
          <FormSection title="Status" description="A draft franchisee becomes active via Edit once onboarded. Deactivate to pause; reactivation is GA-only.">
            <div className="row ai-c jc-sb" style={{ padding: 16, border: "1.5px solid var(--line)", borderRadius: "var(--r)" }}>
              <div>
                <div style={{ ...T.primary(1.3) }}>
                  {fr.status === "active" ? "Deactivate franchisee" : fr.status === "inactive" ? "Reactivate franchisee" : "Franchisee status"}
                </div>
                <div style={{ ...T_MUTED, marginTop: 4 }}>
                  {fr.status === "active" ? "Pauses all outlets and removes CM/AM/OM write access. Reversible."
                    : fr.status === "inactive" ? "Restores operations and write access for this franchisee."
                    : "In draft (onboarding). Set it active via Edit once milestones are met."}
                </div>
              </div>
              {fr.status === "active"   && <Btn variant="danger"  onClick={() => { setConfirmReason(""); setConfirm({ variant: "deactivate" }); }}>Deactivate…</Btn>}
              {fr.status === "inactive" && <Btn variant="primary" onClick={() => { setConfirmReason(""); setConfirm({ variant: "reactivate" }); }}>Reactivate</Btn>}
              {fr.status === "draft"    && <StatusPill status={fr.status} kind="franchisee"/>}
            </div>
          </FormSection>

          <FormSection title="Move to another franchisee" description="Transfers outlets + users to another active franchisee in the same country. Orders and customers are preserved.">
            <div className="row ai-c jc-sb" style={{ padding: 16, border: "1.5px solid var(--line)", borderRadius: "var(--r)" }}>
              <div style={{ ...T_MUTED }}>Same country ({_countryLabel(fr.country_id)}).</div>
              <Btn variant="secondary" onClick={() => { setConfirmReason(""); setMoveTargetFr(""); setConfirm({ variant: "move" }); }}>Move country…</Btn>
            </div>
          </FormSection>

          <DeleteSection
            entityLabel="franchisee"
            soft={fr.status !== "draft"}
            note={fr.status === "draft"
              ? "Permanently delete this draft franchisee. Nothing is live yet. Blocked if it already has outlets."
              : "Terminate + decommission this franchisee (soft-delete). Closes outlets, disables users; historical data retained. GA-only, and blocked while it has outlets."}
            gate={_canDelete(role, { status: fr.status, isReferenced: outletsOfFr.length > 0 })}
            confirmBody={fr.status === "draft"
              ? <div>Permanently delete the draft franchisee <strong>{fr.display_name}</strong>? Writes to <strong>audit_logs</strong>.</div>
              : <div>Terminating <strong>{fr.display_name}</strong> closes all outlets, disables all users and stops payment processing. Historical data is retained. Writes to <strong>audit_logs</strong>.</div>}
            onDelete={() => {
              setActionToast(`Logged to audit_logs · franchisee ${fr.id} ${fr.status === "draft" ? "deleted" : "terminated"}`);
              setTimeout(() => { setActionToast(null); _go("ga-franchisees"); }, 1400);
            }}
          />
        </>
      )}

      <ConfirmModal
        open={!!confirm}
        destructive={!!confirm && confirm.variant !== "reactivate"}
        title={
          confirm && confirm.variant === "move" ? `Move ${fr.display_name} to another franchisee?`
          : confirm && confirm.variant === "reactivate" ? `Reactivate ${fr.display_name}?`
          : `Deactivate ${fr.display_name}?`
        }
        body={
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {confirm && confirm.variant === "move"
              ? <div>Outlets + users transfer to the new franchisee. Orders + customers are preserved. Writes to <strong>audit_logs</strong>.</div>
              : confirm && confirm.variant === "reactivate"
              ? <div>Restores operations: outlets resume and CM / AM / OM users regain write access. Writes to <strong>audit_logs</strong>.</div>
              : <div>Deactivating this franchisee pauses operations: outlets stop accepting new orders and CM / AM / OM users lose write access. You can reactivate later. Writes to <strong>audit_logs</strong>.</div>}
            {confirm && confirm.variant === "move" && (
              <Field label="Target franchisee" hint={`Same country (${_countryLabel(fr.country_id)}); current franchisee excluded.`}>
                <Select value={moveTargetFr} onChange={setMoveTargetFr} placeholder="Choose target franchisee">
                  <option value="">— Choose target —</option>
                  {SB3_FRANCHISEES
                    .filter(f => f.country_id === fr.country_id && f.id !== fr.id && f.status === "active")
                    .map(f => <option key={f.id} value={f.id}>{f.display_name}</option>)}
                </Select>
              </Field>
            )}
            {confirm && confirm.variant !== "reactivate" && (
              <>
                <Field label="Reason" required>
                  <Textarea rows={3} value={confirmReason} onChange={setConfirmReason} placeholder="Explain why this action is being taken (required)"/>
                </Field>
                {!confirmReason.trim() && (
                  <div style={{ ...T_MUTED, fontSize: 12 }}>A reason is required.</div>
                )}
              </>
            )}
          </div>
        }
        confirmLabel={
          confirm && confirm.variant === "move" ? "Move country"
          : confirm && confirm.variant === "reactivate" ? "Reactivate franchisee"
          : "Deactivate franchisee"
        }
        onCancel={() => { setConfirm(null); setConfirmReason(""); setMoveTargetFr(""); }}
        onConfirm={() => {
          if (confirm.variant !== "reactivate" && !confirmReason.trim()) return;
          if (confirm.variant === "move" && !moveTargetFr) return;
          const v = confirm.variant;
          const reasonShort = confirmReason.trim() ? ` · "${confirmReason.trim().length > 40 ? confirmReason.trim().slice(0, 40) + "…" : confirmReason.trim()}"` : "";
          setConfirm(null); setConfirmReason(""); setMoveTargetFr("");
          const verb = v === "move" ? "moved" : v === "reactivate" ? "reactivated" : "deactivated";
          setActionToast(`Logged to audit_logs · franchisee ${fr.id} ${verb}${reasonShort}`);
          setTimeout(() => setActionToast(null), 3200);
        }}
      />

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

Object.assign(window, { FranchiseesList, FranchiseeEdit, FranchiseeDetail });
})();
