// =================================================================
// pw-screens-op-reader-take.jsx — Operator · Reader take (payments phase 7, migration 0061).
//
// DropCap collects every reader payment (coin top-ups, subscription charges) and keeps a take — 25%
// by default — crediting the rest to the publisher under the usual hold. The take is a dated rate: a
// platform default plus per-publisher overrides. Rates are never edited: a new row takes over from its
// effective date, and every payment already split keeps the rate it was split at.
//
// Any staff member can read (a scoped manager sees the default and their own publishers); only a
// superadmin can set a rate or record a refund. The API refuses a manager's write with a 403.
// =================================================================

const readerTakeNotify = (msg, tone) => (window.__toast ? window.__toast(msg, tone) : null);
const RT_STATE_TONE = { in_force: "good", scheduled: "accent", superseded: "neutral" };
const rtPct = (bps) => (bps === null || bps === undefined ? "—" : `${(Number(bps) / 100).toFixed(Number(bps) % 100 ? 2 : 0)}%`);
const rtMinor = (minor, currency) => fmt.currency(Number(minor || 0) / 100, currency || "USD");

const ReaderRefundModal = ({ payment, onClose, onDone }) => {
  const [f, setF] = React.useState({ refund_reference: "", amount: "", reason: "" });
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { setF({ refund_reference: "", amount: "", reason: "" }); }, [payment && payment.credit_id]);
  if (!payment) return null;
  const remaining = Number(payment.gross_minor) - Number(payment.refunded_minor || 0);
  const amountMinor = f.amount === "" ? remaining : Math.round(Number(f.amount) * 100);
  const ok = f.refund_reference.trim() && amountMinor > 0 && amountMinor <= remaining && !busy;
  const save = async () => {
    setBusy(true);
    const pubShare = Math.round((amountMinor * Number(payment.publisher_minor)) / Number(payment.gross_minor));
    try {
      await window.__api.write("adminRefundReaderPayment",
        (d) => ({ ...d, readerPayments: (d.readerPayments || []).map((p) => p.credit_id === payment.credit_id
          ? { ...p, refunded_minor: Number(p.refunded_minor || 0) + amountMinor, refunded_publisher_minor: Number(p.refunded_publisher_minor || 0) + pubShare } : p) }),
        { params: { credit_id: payment.credit_id },
          body: { refund_reference: f.refund_reference.trim(), ...(f.amount !== "" ? { amount_minor: amountMinor } : {}), ...(f.reason.trim() ? { reason: f.reason.trim() } : {}) },
          ok: "Refund recorded", err: "Couldn’t record the refund" });
      onDone(); onClose();
    } catch (e) { /* write() has already said why */ } finally { setBusy(false); }
  };
  return (
    <Modal open onClose={onClose} title={`Record refund · ${payment.gateway_txn_id}`} width={480}
      footer={<><Btn kind="secondary" onClick={onClose}>Cancel</Btn><Btn kind="primary" disabled={!ok} onClick={save}>{busy ? "Recording…" : "Record refund"}</Btn></>}>
      <div style={{ fontSize: 13, color: "var(--ink-2)", marginBottom: 12, lineHeight: 1.5 }}>
        Return the money in the {payment.gateway === "payu" ? "PayU" : "Stripe"} dashboard first, then record it here. The refund is
        split in the original proportion: the publisher's share and DropCap's take both go down, as compensating entries.
      </div>
      <FormField label="Gateway refund id" hint="The refund's id in the gateway, or your own reference. Recording the same one twice is one refund.">
        <Input value={f.refund_reference} maxLength={200} onChange={(e) => setF({ ...f, refund_reference: e.target.value })}/>
      </FormField>
      <FormField label={`Amount (${payment.currency})`} hint={`Blank refunds everything left: ${rtMinor(remaining, payment.currency)}.`}>
        <Input type="number" step="0.01" value={f.amount} onChange={(e) => setF({ ...f, amount: e.target.value })}/>
      </FormField>
      <FormField label="Reason"><Input value={f.reason} maxLength={500} onChange={(e) => setF({ ...f, reason: e.target.value })}/></FormField>
      {amountMinor > remaining && <div style={{ fontSize: 12.5, color: "var(--bad)" }}>More than is left to refund.</div>}
    </Modal>
  );
};

// The rate history table. Shared with the Commission rates screen's Reader take tab.
const ReaderTakeRatesCard = ({ rates }) => (
  <Card title="Rates" subtitle="The default, and publishers on their own rate." padded={false}>
    <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line-2)" }}>
      <ListToolbar list={rates} placeholder="Search publisher or note"
        filters={[
          { key: "kind", label: "Kinds", options: [{ label: "Default", value: "default" }, { label: "Override", value: "override" }] },
          { 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: "publisher_name", label: "Applies to", sortable: true, render: (r) => r.kind === "default"
          ? <strong style={{ color: "var(--ink)" }}>Every publisher (default)</strong> : (r.publisher_name || r.publisher_id) },
        { key: "take_bps", label: "DropCap keeps", sortable: true, align: "right", render: (r) => r.take_bps === null
          ? <span style={{ color: "var(--muted)" }}>back to default</span> : <strong>{rtPct(r.take_bps)}</strong> },
        { key: "publisher_keeps", label: "Publisher keeps", align: "right", render: (r) => r.take_bps === null ? "—" : rtPct(10000 - r.take_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={RT_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 || ""}</span> },
      ]}/>
  </Card>
);

const OpReaderTakeScreen = ({ data }) => {
  const rates = useListQuery("adminListReaderTakeRates", { sort: "effective_from", dir: "desc", filters: { kind: "", status: "" }, watch: data.readerTakeRates });
  const payments = useListQuery("adminListReaderPayments", { sort: "created_at", dir: "desc", filters: { gateway: "", source_kind: "" }, watch: data.readerPayments });
  const [refunding, setRefunding] = React.useState(null);
  React.useEffect(() => { if (rates.error) readerTakeNotify("Could not load reader take rates.", "bad"); }, [rates.error]);
  React.useEffect(() => { if (payments.error) readerTakeNotify("Could not load reader payments.", "bad"); }, [payments.error]);
  const def = (rates.extra && rates.extra.default_in_force)
    || (data.readerTakeRates || []).find((r) => r.kind === "default" && r.status === "in_force");

  return (
    <Screen>
      <PageHead icon={IconCoin} title="Reader take"
        subtitle="DropCap's share of what readers pay publishers: coin top-ups and subscription charges. The publisher keeps the rest, under the usual hold."
        actions={<Btn kind="primary" size="sm" onClick={() => window.__nav("reader-take-create")}>Set a rate</Btn>}/>
      <Banner tone="info" icon={<IconInfo size={15}/>}>
        Superadmin only: managers can view rates and payments but cannot change them. A rate is never edited — a new one takes
        over from its effective date, and every payment already split keeps the rate it was split at.
        Readers pay through the publisher's registered country: India on PayU, everywhere else on Stripe.
      </Banner>
      <KpiGrid>
        <StatCard label="Default take" value={def ? rtPct(def.take_bps) : "—"} sub={def ? `publisher keeps ${rtPct(10000 - def.take_bps)}` : "No default in force"}/>
        <StatCard label="Overrides" value={fmt.number((rates.rows || []).filter((r) => r.kind === "override" && r.status !== "superseded").length)} sub="in force or scheduled (this page)"/>
      </KpiGrid>
      <ReaderTakeRatesCard rates={rates}/>
      <Card title="Split reader payments" subtitle="Each confirmed payment, split once at the rate in force when it was confirmed." padded={false}>
        <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--line-2)" }}>
          <ListToolbar list={payments} placeholder="Search gateway id or publisher"
            filters={[
              { key: "gateway", label: "Gateways", options: [{ label: "Stripe", value: "stripe" }, { label: "PayU", value: "payu" }] },
              { key: "source_kind", label: "Kinds", options: [{ label: "Coin top-up", value: "wallet_topup" }, { label: "Subscription", value: "subscription_charge" }] },
            ]}/>
        </div>
        <DataTable rows={payments.rows} keyField="credit_id" server={payments.server} empty="No reader payments yet."
          columns={[
            { key: "created_at", label: "Confirmed", sortable: true, render: (r) => <span style={{ fontFamily: "var(--mono)", fontSize: 12.5 }}>{fmt.date(r.created_at)}</span> },
            { key: "publisher_name", label: "Publisher", sortable: true, render: (r) => r.publisher_name || r.publisher_id },
            { key: "source_kind", label: "Kind", render: (r) => r.source_kind === "wallet_topup" ? "Coin top-up" : "Subscription" },
            { key: "gateway", label: "Gateway", render: (r) => <span style={{ fontFamily: "var(--mono)", fontSize: 12 }}>{r.gateway} · {r.gateway_txn_id}</span> },
            { key: "gross_minor", label: "Reader paid", sortable: true, align: "right", render: (r) => rtMinor(r.gross_minor, r.currency) },
            { key: "publisher_minor", label: "Publisher", align: "right", render: (r) => <span>{rtMinor(r.publisher_minor, r.currency)} <span style={{ color: "var(--muted)", fontSize: 11.5 }}>({rtPct(10000 - r.take_bps)})</span></span> },
            { key: "platform_minor", label: "DropCap", align: "right", render: (r) => rtMinor(r.platform_minor, r.currency) },
            { key: "publisher_available_at", label: "Releases", render: (r) => <span style={{ fontSize: 12.5 }}>{fmt.date(r.publisher_available_at)}</span> },
            { key: "refunded_minor", label: "Refunded", align: "right", render: (r) => Number(r.refunded_minor) > 0 ? rtMinor(r.refunded_minor, r.currency) : "—" },
            { key: "actions", label: "", render: (r) => Number(r.refunded_minor || 0) >= Number(r.gross_minor) ? <Pill tone="neutral">refunded</Pill>
              : <Btn kind="secondary" size="sm" onClick={(e) => { e.stopPropagation(); setRefunding(r); }}>Refund</Btn> },
          ]}/>
      </Card>
      <ReaderRefundModal payment={refunding} onClose={() => setRefunding(null)} onDone={payments.reload}/>
    </Screen>
  );
};

const ReaderTakeCreateScreen = ({ data }) => {
  useRefList(["listPublishers"]);
  const [f, setF] = React.useState({ publisher_id: "", mode: "rate", take: "", effective_from: "", note: "" });
  const [busy, setBusy] = React.useState(false);
  const set = (p) => setF((x) => ({ ...x, ...p }));
  const pubs = data.publishers || [];
  const pct = f.take === "" ? NaN : Number(f.take);
  const backToDefault = f.publisher_id && f.mode === "default";
  const rateOk = backToDefault || (pct >= 0 && pct <= 100);
  const future = f.effective_from ? new Date(f.effective_from) : null;
  const dateOk = !future || future.getTime() >= Date.now() - 60_000;
  const canSave = rateOk && dateOk && f.note.trim() && !busy;
  const save = async () => {
    setBusy(true);
    const take_bps = backToDefault ? null : Math.round(pct * 100);
    const body = {
      ...(f.publisher_id ? { publisher_id: f.publisher_id } : {}),
      take_bps,
      ...(future ? { effective_from: future.toISOString() } : {}),
      note: f.note.trim(),
    };
    const eff = future ? future.toISOString() : new Date().toISOString();
    const scheduled = Boolean(future && future.getTime() > Date.now());
    const pub = pubs.find((p) => (p.publisher_id || p.id) === f.publisher_id);
    const row = { rate_id: "rt_" + Math.random().toString(36).slice(2, 8), kind: f.publisher_id ? "override" : "default", publisher_id: f.publisher_id || null,
      publisher_name: pub ? pub.name : null, take_bps, effective_from: eff, superseded_at: null, status: scheduled ? "scheduled" : "in_force",
      note: body.note, created_by: "you", created_at: new Date().toISOString() };
    const same = (r) => (r.publisher_id || null) === row.publisher_id;
    try {
      await window.__api.write("adminCreateReaderTakeRate",
        // Demo: the new in-force row supersedes the previous one for the same publisher (or the default).
        (d) => ({ ...d, readerTakeRates: [row, ...(d.readerTakeRates || []).map((r) => (!scheduled && r.status === "in_force" && same(r) ? { ...r, status: "superseded", superseded_at: eff } : r))] }),
        { body, ok: scheduled ? "Rate scheduled" : "Rate in force", err: "Couldn’t set that rate" });
      pwListsStale();
      window.__nav("op-reader-take");
    } catch (e) { /* write() has already said why */ } finally { setBusy(false); }
  };
  return (
    <FormFrame page={{ icon: IconCoin, back: "Reader take" }} kind="modal" open onClose={pwBackTo("op-reader-take")} title="Set a reader take rate" width={520}
      footer={<><Btn kind="secondary" onClick={pwBackTo("op-reader-take")}>Cancel</Btn><Btn kind="primary" disabled={!canSave} onClick={save}>{busy ? "Saving…" : "Save rate"}</Btn></>}>
      <FormField label="Applies to" hint="Blank sets the platform default; choosing a publisher sets their own rate.">
        <Select value={f.publisher_id} onChange={(v) => set({ publisher_id: v, mode: "rate" })}
          options={[{ label: "Every publisher (default)", value: "" }, ...pubs.map((p) => ({ label: p.name, value: p.publisher_id || p.id }))]}/>
      </FormField>
      {f.publisher_id && (
        <FormField label="Rate">
          <SegmentedControl value={f.mode} onChange={(v) => set({ mode: v })}
            options={[{ label: "Own rate", value: "rate" }, { label: "Back to the default", value: "default" }]}/>
        </FormField>
      )}
      {!backToDefault && (
        <FormField label="DropCap keeps (%)" hint={Number.isFinite(pct) && rateOk ? `The publisher keeps ${(100 - pct).toFixed(2).replace(/\.00$/, "")}% of each reader payment.` : "Between 0 and 100."}>
          <Input type="number" step="0.01" min="0" max="100" value={f.take} placeholder="25" onChange={(e) => set({ take: e.target.value })}/>
        </FormField>
      )}
      <FormField label="Effective from" hint="Blank: now. Never in the past — payments already split keep their rate either way.">
        <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>
      {!dateOk && <div style={{ fontSize: 12.5, color: "var(--bad)" }}>Effective date is in the past.</div>}
    </FormFrame>
  );
};

Object.assign(window, { OpReaderTakeScreen, ReaderTakeCreateScreen, ReaderTakeRatesCard });
