// =================================================================
// pw-screens-op-rates.jsx — Operator · Commission rates (PKG-15 remainder, migration 0068).
//
// One screen for everything DropCap prices a publisher at:
//   * Rate card — DropCap / selling publisher / hosting publisher shares for direct, brokered and own
//     (platform_commission_rates, 0029/0034). A change is a NEW row with a FUTURE effective date; past
//     rows are never edited or deleted, and an event already recorded keeps the share frozen onto it.
//   * Publisher overrides — a publisher priced outside the rate card (0030 §2). Applies from now.
//   * Reader take — DropCap's share of reader payments (0061), the same rates as the Reader take screen.
//
// Any staff member can read (a scoped manager sees only their publishers' overrides); only a superadmin
// can change anything. The API refuses a manager's write with a 403. The `partner` context belongs to
// the channel-partner work and is not shown or editable here.
// =================================================================

const ratesNotify = (msg, tone) => (window.__toast ? window.__toast(msg, tone) : null);
const RC_STATE_TONE = { in_force: "good", scheduled: "accent", superseded: "neutral" };
const RC_CONTEXTS = [
  { value: "direct", label: "Direct", hint: "DropCap found the advertiser; a publisher hosts it." },
  { value: "brokered", label: "Brokered", hint: "One publisher sold onto another publisher's inventory." },
  { value: "own", label: "Own", hint: "A publisher's own advertiser on their own site." },
];
const rcContextLabel = (c) => (RC_CONTEXTS.find((x) => x.value === c) || { label: c }).label;
const rcPct = (bps) => (bps === null || bps === undefined ? "—" : `${(Number(bps) / 100).toFixed(Number(bps) % 100 ? 2 : 0)}%`);
const rcIsManager = (session) => String((session && session.user && session.user.roleLabel) || "").toLowerCase() === "manager";
// PRD §5 guidance bands for brokered (D1). A warning at the point of entry, never a refusal (0029).
const RC_BROKERED_BANDS = { dropcap: [2000, 2500], seller: [1500, 2000], host: [5500, 6500] };

// The first instant of the next UTC month, as a datetime-local value in the viewer's time zone. 0034
// explains why rate changes start at a month boundary: no split days, one base per volume-ladder month.
const rcNextMonthLocal = () => {
  const now = new Date();
  const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1));
  const pad = (n) => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
};

const RatesAccessNote = ({ manager }) => (
  <Banner tone="info" icon={<IconInfo size={15}/>}>
    {manager ? "Read only. " : ""}Superadmin only: managers can view rates but cannot change them. A rate-card change is scheduled
    for a future date and never edits a past rate; every event already recorded keeps the share it was recorded at.
  </Banner>
);

// Demo: the list's `in_force` summary, derived from the fixtures the way the API derives it.
const rcInForce = (list, fixtures) => {
  const fromApi = list.extra && list.extra.in_force;
  if (Array.isArray(fromApi) && fromApi.length) return fromApi;
  return RC_CONTEXTS.map((c) => (fixtures || []).find((r) => r.context === c.value && r.status === "in_force")).filter(Boolean);
};

const OpRatesScreen = ({ data }) => {
  const { session } = useSession();
  const manager = rcIsManager(session);
  const [tab, setTab] = React.useState("card");
  const rates = useListQuery("adminListCommissionRates", { stateKey: "rc", sort: "effective_from", dir: "desc", filters: { context: "", status: "" }, watch: data.commissionRates, enabled: tab === "card" });
  const overrides = useListQuery("adminListCommissionOverrides", { stateKey: "ov", sort: "set_at", dir: "desc", watch: data.commissionOverrides, enabled: tab === "overrides" });
  const take = useListQuery("adminListReaderTakeRates", { stateKey: "rt", sort: "effective_from", dir: "desc", filters: { kind: "", status: "" }, watch: data.readerTakeRates, enabled: tab === "reader" });
  React.useEffect(() => { if (rates.error) ratesNotify("Could not load the rate card.", "bad"); }, [rates.error]);
  React.useEffect(() => { if (overrides.error) ratesNotify("Could not load publisher overrides.", "bad"); }, [overrides.error]);
  React.useEffect(() => { if (take.error) ratesNotify("Could not load reader take rates.", "bad"); }, [take.error]);
  const inForce = rcInForce(rates, data.commissionRates);

  const action = manager ? null
    : tab === "card" ? <Btn kind="primary" size="sm" onClick={() => window.__nav("commission-rate-create")}>Schedule a rate</Btn>
    : tab === "overrides" ? <Btn kind="primary" size="sm" onClick={() => window.__nav("commission-override-create")}>Set an override</Btn>
    : tab === "agent" ? <Btn kind="primary" size="sm" onClick={() => window.__nav("agent-take-create")}>Schedule an agent take rate</Btn>
    : <Btn kind="primary" size="sm" onClick={() => window.__nav("reader-take-create")}>Set a reader take rate</Btn>;

  return (
    <Screen>
      <PageHead icon={IconCoin} title="Commission rates"
        subtitle="What DropCap keeps and what publishers are paid: the rate card, per-publisher overrides, and the take on reader payments."
        actions={action}/>
      <RatesAccessNote manager={manager}/>
      <KpiGrid>
        {RC_CONTEXTS.map((c) => {
          const r = inForce.find((x) => x.context === c.value);
          return <StatCard key={c.value} label={`${c.label} · DropCap`} value={r ? rcPct(r.dropcap_bps) : "—"}
            sub={r ? `host ${rcPct(r.host_bps)}${Number(r.seller_bps) ? ` · seller ${rcPct(r.seller_bps)}` : ""}` : "No rate in force"}/>;
        })}
      </KpiGrid>
      <Tabs value={tab} onChange={setTab} items={[
        { label: "Rate card", value: "card" },
        { label: "Publisher overrides", value: "overrides" },
        { label: "Reader take", value: "reader" },
        { label: "Agent take", value: "agent" },
      ]}/>

      {tab === "card" && (
        <Card title="Rate card history" subtitle="Each context's rate is the latest row that has started. Scheduled rows take over on their date." padded={false}>
          <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line-2)" }}>
            <ListToolbar list={rates} placeholder="Search context or note"
              filters={[
                { key: "context", label: "Contexts", options: RC_CONTEXTS.map((c) => ({ label: c.label, value: c.value })) },
                { key: "status", label: "States", options: statusOptions(["in_force", "scheduled", "superseded"]) },
              ]}/>
          </div>
          <DataTable rows={rates.rows} keyField="rate_id" server={rates.server} empty="No rates match."
            columns={[
              { key: "context", label: "Context", sortable: true, render: (r) => <strong style={{ color: "var(--ink)" }}>{rcContextLabel(r.context)}</strong> },
              { key: "dropcap_bps", label: "DropCap", align: "right", render: (r) => <strong>{rcPct(r.dropcap_bps)}</strong> },
              { key: "seller_bps", label: "Seller", align: "right", render: (r) => r.context === "brokered" ? rcPct(r.seller_bps) : "—" },
              { key: "host_bps", label: "Host", align: "right", render: (r) => rcPct(r.host_bps) },
              { key: "effective_from", label: "From", sortable: true, render: (r) => <span style={{ fontFamily: "var(--mono)", fontSize: 12.5 }}>{fmt.date(r.effective_from)}</span> },
              { key: "status", label: "State", render: (r) => <Pill tone={RC_STATE_TONE[r.status] || "neutral"}>{String(r.status || "").replace("_", " ")}</Pill> },
              { key: "note", label: "Note", render: (r) => <span style={{ fontSize: 12.5, color: "var(--muted)" }}>{r.note || ""}{r.created_by ? ` · ${r.created_by}` : ""}</span> },
            ]}/>
        </Card>
      )}

      {tab === "overrides" && (
        <Card title="Publisher overrides" subtitle="Publishers paid a fixed share of every event they host, in every context, instead of the rate card. Applies from when it was set; clearing returns them to the rate card." padded={false}>
          <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line-2)" }}>
            <ListToolbar list={overrides} placeholder="Search publisher"/>
          </div>
          <DataTable rows={overrides.rows} keyField="publisher_id" server={overrides.server} empty="No publisher is on an override."
            columns={[
              { key: "publisher_name", label: "Publisher", sortable: true, render: (r) => <strong style={{ color: "var(--ink)" }}>{r.publisher_name || r.publisher_id}</strong> },
              { key: "host_bps", label: "Publisher keeps", sortable: true, align: "right", render: (r) => <strong>{rcPct(r.host_bps)}</strong> },
              { key: "set_at", label: "Since", sortable: true, render: (r) => <span style={{ fontFamily: "var(--mono)", fontSize: 12.5 }}>{fmt.date(r.set_at)}</span> },
              { key: "actions", label: "", render: (r) => manager ? null
                : <Btn kind="secondary" size="sm" onClick={(e) => { e.stopPropagation(); window.__nav("commission-override-create", r.publisher_id); }}>Change</Btn> },
            ]}/>
        </Card>
      )}

      {tab === "reader" && <ReaderTakeRatesCard rates={take}/>}
      {/* Agent payments (0069): DropCap's share of per-article agent purchases. */}
      {tab === "agent" && <AgentTakeRatesCard data={data}/>}
    </Screen>
  );
};

const CommissionRateCreateScreen = ({ data }) => {
  const [f, setF] = React.useState({ context: "direct", dropcap: "", seller: "0", host: "", effective_from: rcNextMonthLocal(), note: "" });
  const [busy, setBusy] = React.useState(false);
  const set = (p) => setF((x) => ({ ...x, ...p }));
  const brokered = f.context === "brokered";
  const bps = (v) => (v === "" || !Number.isFinite(Number(v)) ? NaN : Math.round(Number(v) * 100));
  const d = bps(f.dropcap), s = brokered ? bps(f.seller) : 0, h = bps(f.host);
  const inRange = [d, s, h].every((x) => Number.isFinite(x) && x >= 0 && x <= 10000);
  const sumOk = inRange && d + s + h === 10000;
  const from = f.effective_from ? new Date(f.effective_from) : null;
  const dateOk = Boolean(from && from.getTime() > Date.now());
  const canSave = sumOk && dateOk && f.note.trim() && !busy;
  const outOfBand = brokered && sumOk && (
    d < RC_BROKERED_BANDS.dropcap[0] || d > RC_BROKERED_BANDS.dropcap[1] ||
    s < RC_BROKERED_BANDS.seller[0] || s > RC_BROKERED_BANDS.seller[1] ||
    h < RC_BROKERED_BANDS.host[0] || h > RC_BROKERED_BANDS.host[1]);
  const current = (data.commissionRates || []).find((r) => r.context === f.context && r.status === "in_force");

  const save = async () => {
    setBusy(true);
    const body = { context: f.context, dropcap_bps: d, seller_bps: s, host_bps: h, effective_from: from.toISOString(), note: f.note.trim() };
    const row = { rate_id: "rc_" + Math.random().toString(36).slice(2, 8), ...body, superseded_at: null, status: "scheduled",
      created_by: "you", created_at: new Date().toISOString() };
    try {
      await window.__api.write("adminCreateCommissionRate",
        (dd) => ({ ...dd, commissionRates: [row, ...(dd.commissionRates || [])] }),
        { body, ok: "Rate scheduled", err: "Couldn’t schedule that rate" });
      pwListsStale();
      window.__nav("op-rates");
    } catch (e) { /* write() has already said why */ } finally { setBusy(false); }
  };

  return (
    <FormFrame page={{ icon: IconCoin, back: "Commission rates" }} kind="modal" open onClose={pwBackTo("op-rates")} title="Schedule a rate-card change" width={540}
      footer={<><Btn kind="secondary" onClick={pwBackTo("op-rates")}>Cancel</Btn><Btn kind="primary" disabled={!canSave} onClick={save}>{busy ? "Saving…" : "Schedule rate"}</Btn></>}>
      <FormField label="Context" hint={(RC_CONTEXTS.find((c) => c.value === f.context) || {}).hint}>
        <Select value={f.context} onChange={(v) => set({ context: v, seller: "0" })} options={RC_CONTEXTS.map((c) => ({ label: c.label, value: c.value }))}/>
      </FormField>
      {current && (
        <div style={{ fontSize: 12.5, color: "var(--muted)", marginBottom: 10 }}>
          In force now: DropCap {rcPct(current.dropcap_bps)}{brokered ? ` · seller ${rcPct(current.seller_bps)}` : ""} · host {rcPct(current.host_bps)}, since {fmt.date(current.effective_from)}.
        </div>
      )}
      <FormField label="DropCap keeps (%)">
        <Input type="number" step="0.01" min="0" max="100" value={f.dropcap} onChange={(e) => set({ dropcap: e.target.value })}/>
      </FormField>
      {brokered && (
        <FormField label="Selling publisher (%)" hint="Only a brokered sale has a seller to pay.">
          <Input type="number" step="0.01" min="0" max="100" value={f.seller} onChange={(e) => set({ seller: e.target.value })}/>
        </FormField>
      )}
      <FormField label="Hosting publisher (%)" hint={inRange ? `Total ${((d + s + h) / 100).toFixed(2).replace(/\.00$/, "")}% — must be exactly 100%.` : "Each share is between 0 and 100."}>
        <Input type="number" step="0.01" min="0" max="100" value={f.host} onChange={(e) => set({ host: e.target.value })}/>
      </FormField>
      <FormField label="Effective from" hint="Must be in the future. Defaults to the start of next month (UTC), so no day or month is split across two rates.">
        <Input type="datetime-local" value={f.effective_from} onChange={(e) => set({ effective_from: e.target.value })}/>
      </FormField>
      <FormField label="Note" hint="Why — recorded with the rate and in the audit trail.">
        <Input value={f.note} maxLength={500} onChange={(e) => set({ note: e.target.value })}/>
      </FormField>
      {inRange && !sumOk && <div style={{ fontSize: 12.5, color: "var(--bad)" }}>Shares must add up to 100%.</div>}
      {f.effective_from && !dateOk && <div style={{ fontSize: 12.5, color: "var(--bad)" }}>Effective date must be in the future.</div>}
      {outOfBand && <div style={{ fontSize: 12.5, color: "var(--warn)" }}>Outside the PRD §5 brokered bands (DropCap 20–25%, seller 15–20%, host 55–65%). Allowed, but check it is intended.</div>}
    </FormFrame>
  );
};

const CommissionOverrideCreateScreen = ({ data, publisherId }) => {
  useRefList(["listPublishers"]);
  const pubs = data.publishers || [];
  const existing = (pid) => (data.commissionOverrides || []).find((o) => o.publisher_id === pid);
  const initial = publisherId && existing(publisherId);
  const [f, setF] = React.useState({ publisher_id: publisherId || "", mode: "set", share: initial ? String(initial.host_bps / 100) : "", note: "" });
  const [busy, setBusy] = React.useState(false);
  const set = (p) => setF((x) => ({ ...x, ...p }));
  const current = f.publisher_id ? existing(f.publisher_id) : null;
  const clearing = f.mode === "clear";
  const pct = f.share === "" ? NaN : Number(f.share);
  const shareOk = clearing || (Number.isFinite(pct) && pct >= 0 && pct <= 100);
  const canSave = f.publisher_id && shareOk && (!clearing || current) && f.note.trim() && !busy;

  const save = async () => {
    setBusy(true);
    const host_bps = clearing ? null : Math.round(pct * 100);
    const body = { publisher_id: f.publisher_id, host_bps, note: f.note.trim() };
    const pub = pubs.find((p) => (p.publisher_id || p.id) === f.publisher_id);
    const now = new Date().toISOString();
    try {
      await window.__api.write("adminSetCommissionOverride",
        (dd) => {
          const rest = (dd.commissionOverrides || []).filter((o) => o.publisher_id !== f.publisher_id);
          return { ...dd, commissionOverrides: clearing ? rest
            : [{ publisher_id: f.publisher_id, publisher_name: pub ? pub.name : (current && current.publisher_name) || null, host_bps, set_at: now, updated_at: now }, ...rest] };
        },
        { body, ok: clearing ? "Override cleared" : "Override set", err: "Couldn’t change the override" });
      pwListsStale();
      window.__nav("op-rates");
    } catch (e) { /* write() has already said why */ } finally { setBusy(false); }
  };

  return (
    <FormFrame page={{ icon: IconCoin, back: "Commission rates" }} kind="modal" open onClose={pwBackTo("op-rates")} title="Publisher commission override" width={520}
      footer={<><Btn kind="secondary" onClick={pwBackTo("op-rates")}>Cancel</Btn><Btn kind="primary" disabled={!canSave} onClick={save}>{busy ? "Saving…" : clearing ? "Clear override" : "Save override"}</Btn></>}>
      <div style={{ fontSize: 13, color: "var(--ink-2)", marginBottom: 12, lineHeight: 1.5 }}>
        An override pays this publisher a fixed share of every ad event they host, in every context, instead of the rate card.
        It applies to events from now on; events already recorded keep the share they were recorded at.
      </div>
      <FormField label="Publisher">
        <Select value={f.publisher_id} onChange={(v) => { const o = existing(v); set({ publisher_id: v, mode: "set", share: o ? String(o.host_bps / 100) : "" }); }}
          options={[{ label: "Choose a publisher", value: "" }, ...pubs.map((p) => ({ label: p.name, value: p.publisher_id || p.id }))]}/>
      </FormField>
      {current && (
        <FormField label="Change">
          <SegmentedControl value={f.mode} onChange={(v) => set({ mode: v })}
            options={[{ label: "New share", value: "set" }, { label: "Back to the rate card", value: "clear" }]}/>
        </FormField>
      )}
      {current && <div style={{ fontSize: 12.5, color: "var(--muted)", marginBottom: 10 }}>On an override now: keeps {rcPct(current.host_bps)} since {fmt.date(current.set_at)}.</div>}
      {!clearing && (
        <FormField label="Publisher keeps (%)" hint={Number.isFinite(pct) && shareOk ? `DropCap keeps ${(100 - pct).toFixed(2).replace(/\.00$/, "")}% (less any seller share on brokered sales).` : "Between 0 and 100."}>
          <Input type="number" step="0.01" min="0" max="100" value={f.share} onChange={(e) => set({ share: e.target.value })}/>
        </FormField>
      )}
      <FormField label="Note" hint="Why — recorded in the audit trail.">
        <Input value={f.note} maxLength={500} onChange={(e) => set({ note: e.target.value })}/>
      </FormField>
    </FormFrame>
  );
};

Object.assign(window, { OpRatesScreen, CommissionRateCreateScreen, CommissionOverrideCreateScreen });
