// =================================================================
// pw-screens-adtiers.jsx — Publisher Admin §7 (showpiece)
//   AdTiersScreen (catalog) · AdDefaultsScreen · AdDebuggerScreen
// =================================================================

const AD_FORMATS = [{ k: "preroll", l: "Pre-roll" }, { k: "midroll", l: "Mid-roll" }, { k: "postroll", l: "Post-roll" }];

// ---------- Nullable numeric field with ∞ (unlimited) toggle ----------
const NullableNumber = ({ label, hint, value, onChange, step = 1, max = 100000, suffix, zeroNote }) => {
  const unlimited = value == null;
  return (
    <div className="pw-nn">
      <div className="pw-nn-head">
        <label className="pw-lbl" style={{ margin: 0 }}>{label}</label>
        <button type="button" className={`pw-inf ${unlimited ? "on" : ""}`} onClick={() => onChange(unlimited ? 0 : null)}>
          ∞ Unlimited
        </button>
      </div>
      <div style={{ opacity: unlimited ? 0.4 : 1, pointerEvents: unlimited ? "none" : "auto" }}>
        <NumberStepper value={unlimited ? 0 : value} onChange={onChange} min={0} max={max} step={step} suffix={suffix}/>
      </div>
      {hint && <div className="pw-nn-hint">{hint}</div>}
      {zeroNote && value === 0 && <div className="pw-nn-hint" style={{ color: "var(--good)" }}>{zeroNote}</div>}
      <style>{`
        .pw-nn { display: flex; flex-direction: column; gap: 8px; }
        .pw-nn-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
        .pw-inf { background: transparent; border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px; font: inherit; font-size: 11.5px; color: var(--muted); cursor: pointer; }
        .pw-inf:hover { border-color: var(--accent); color: var(--accent); }
        .pw-inf.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-strong); font-weight: 500; }
        .pw-nn-hint { font-size: 11.5px; color: var(--muted); }
      `}</style>
    </div>
  );
};

// ---------- Live preview block (used in tier editor) ----------
const TierPreview = ({ tier }) => (
  <div className="pw-tier-preview">
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, marginBottom: 8 }}>
      <span style={{ fontSize: 11.5, textTransform: "uppercase", letterSpacing: ".05em", color: "var(--muted)" }}>Live preview</span>
      <AdLoadBar tier={tier} width={120}/>
    </div>
    <div style={{ fontSize: 14, color: "var(--ink)", lineHeight: 1.5, fontWeight: 500 }}>{adTierSummary(tier)}</div>
    <style>{`
      .pw-tier-preview { border: 1px solid var(--accent); background: var(--accent-soft); border-radius: var(--r-md); padding: 14px; }
      body[data-theme="dark"] .pw-tier-preview { background: color-mix(in oklab, var(--accent) 14%, var(--panel)); }
    `}</style>
  </div>
);

// ---------- Tier create/edit drawer ----------
const TierDrawer = ({ open, onClose, tier, existingCodes, onSave }) => {
  const blank = { tier_code: "", name: "", rank: 100, max_ads_per_session: 3, max_ad_seconds: 60, min_gap_seconds: 120, allowed_formats: ["preroll", "midroll"], skippable_after_seconds: 5, status: "active" };
  const [f, setF] = React.useState(blank);
  const isEdit = !!tier;
  React.useEffect(() => { if (open) setF(tier ? { ...tier } : { ...blank }); }, [open, tier]);
  const set = (patch) => setF(prev => ({ ...prev, ...patch }));
  const toggleFmt = (k) => set({ allowed_formats: f.allowed_formats.includes(k) ? f.allowed_formats.filter(x => x !== k) : [...f.allowed_formats, k] });

  const slugError = !isEdit && f.tier_code && existingCodes.includes(f.tier_code);
  const canSave = f.name && f.tier_code && !slugError;

  return (
    <Drawer open={open} onClose={onClose} title={isEdit ? `Edit · ${tier.name}` : "New ad tier"} width={560}
      footer={<><Btn kind="secondary" onClick={onClose}>Cancel</Btn>
        <Btn kind="primary" disabled={!canSave} onClick={() => { onSave(f, isEdit); onClose(); }}>{isEdit ? "Save tier" : "Create tier"}</Btn></>}>
      <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
        <TierPreview tier={f}/>

        <FieldGroup cols={2}>
          <div>
            <label className="pw-lbl">Tier code {isEdit && <span style={{ color: "var(--muted-2)", fontWeight: 400 }}>(locked)</span>}</label>
            <Input value={f.tier_code} disabled={isEdit} placeholder="standard"
              onChange={e => set({ tier_code: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_") })}/>
            {slugError && <div style={{ fontSize: 11.5, color: "var(--bad)", marginTop: 4 }}>Code already exists.</div>}
          </div>
          <div>
            <label className="pw-lbl">Name</label>
            <Input value={f.name} onChange={e => set({ name: e.target.value })} placeholder="Standard"/>
          </div>
        </FieldGroup>

        <div>
          <label className="pw-lbl">Rank</label>
          <NumberStepper value={f.rank} onChange={v => set({ rank: v })} min={0} max={1000} step={10}/>
          <div className="pw-nn-hint" style={{ marginTop: 6, fontSize: 11.5, color: "var(--muted)" }}>Lower = fewer ads; 0 = ad-free. Rank drives least-ads-wins.</div>
        </div>

        <div className="pw-grid2">
          <NullableNumber label="Max ads / session" value={f.max_ads_per_session} onChange={v => set({ max_ads_per_session: v })}/>
          <NullableNumber label="Max ad seconds" value={f.max_ad_seconds} onChange={v => set({ max_ad_seconds: v })} step={5}
            hint="Total ad time per session." zeroNote="0 seconds = ad-free."/>
          <NullableNumber label="Min gap (seconds)" value={f.min_gap_seconds} onChange={v => set({ min_gap_seconds: v })} step={15}/>
          <NullableNumber label="Skippable after (s)" value={f.skippable_after_seconds} onChange={v => set({ skippable_after_seconds: v })} step={1}/>
        </div>

        <div>
          <label className="pw-lbl">Allowed formats</label>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            {AD_FORMATS.map(fmtO => {
              const on = f.allowed_formats.includes(fmtO.k);
              return <button key={fmtO.k} type="button" className={`pw-chip ${on ? "on" : ""}`} onClick={() => toggleFmt(fmtO.k)}>
                {on && <IconCheck size={12}/>}{fmtO.l}</button>;
            })}
          </div>
        </div>

        <div>
          <label className="pw-lbl">Status</label>
          <SegmentedControl value={f.status} onChange={v => set({ status: v })} options={[{ label: "Active", value: "active" }, { label: "Retired", value: "retired" }]}/>
        </div>
      </div>
      <style>{`
        .pw-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
        @media (max-width: 560px) { .pw-grid2 { grid-template-columns: 1fr; } }
        .pw-chip { display: inline-flex; align-items: center; gap: 5px; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; padding: 7px 13px; font: inherit; font-size: 12.5px; color: var(--ink-2); cursor: pointer; transition: all .12s; }
        .pw-chip:hover { border-color: var(--accent); }
        .pw-chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-strong); font-weight: 500; }
      `}</style>
    </Drawer>
  );
};

// =================================================================
// §7a Ad tier catalog
// =================================================================
const AdTiersScreen = ({ data, setData }) => {
  const [drawer, setDrawer] = React.useState(false);
  const [editing, setEditing] = React.useState(null);
  const tiers = [...data.adTiers].sort((a, b) => a.rank - b.rank);

  const save = (f, isEdit) => {
    if (isEdit) {
      window.__api.write("updateAdTier",
        d => ({ ...d, adTiers: d.adTiers.map(t => t.tier_code === f.tier_code ? { ...t, ...f } : t) }),
        { params: { tier_code: f.tier_code }, body: f, ok: `Updated “${f.name}”`, err: "Couldn’t update tier" });
    } else {
      window.__api.write("createAdTier",
        d => ({ ...d, adTiers: [...d.adTiers, f] }),
        { body: f, ok: `Created “${f.name}”`, err: "Couldn’t create tier" });
    }
  };

  const seedPresets = () => {
    const presets = [
      { tier_code: "no_ads", name: "Ad-free", rank: 0, max_ads_per_session: 0, max_ad_seconds: 0, min_gap_seconds: null, allowed_formats: [], skippable_after_seconds: null, status: "active" },
      { tier_code: "light", name: "Light", rank: 50, max_ads_per_session: 1, max_ad_seconds: 30, min_gap_seconds: 300, allowed_formats: ["preroll"], skippable_after_seconds: 5, status: "active" },
      { tier_code: "standard", name: "Standard", rank: 100, max_ads_per_session: 3, max_ad_seconds: 60, min_gap_seconds: 120, allowed_formats: ["preroll", "midroll"], skippable_after_seconds: 5, status: "active" },
    ];
    setData(d => ({ ...d, adTiers: presets }));
    window.__toast("Starter presets created");
  };

  return (
    <Screen>
      <PageHead icon={IconAd} title="Ad Tiers" subtitle="Reusable ad levels. Lower rank = fewer ads; least-ads-wins at decision time."
        actions={<Btn kind="primary" icon={<IconPlus size={15}/>} onClick={() => { setEditing(null); setDrawer(true); }}>New tier</Btn>}/>

      {tiers.length === 0 ? (
        <Card>
          <EmptyState icon={<IconAd size={32}/>} title="No ad tiers yet"
            description="Create reusable ad levels readers' plans and access classes resolve against."
            action={<div style={{ display: "flex", gap: 8 }}>
              <Btn kind="primary" onClick={seedPresets}>Create starter presets</Btn>
              <Btn kind="secondary" onClick={() => { setEditing(null); setDrawer(true); }}>New tier</Btn>
            </div>}/>
        </Card>
      ) : (
        <Card padded={false}>
          <DataTable rows={tiers} keyField="tier_code" defaultSort={{ key: "rank", dir: "asc" }} onRowClick={(r) => { setEditing(r); setDrawer(true); }}
            columns={[
              { key: "name", label: "Tier", render: r => (
                <div><div style={{ fontWeight: 600, color: "var(--ink)" }}>{r.name}</div>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--muted)" }}>{r.tier_code}</div></div>) },
              { key: "rank", label: "Rank", align: "right", render: r => <span style={{ fontFamily: "var(--mono)", fontSize: 13 }}>{r.rank}</span> },
              { key: "load", label: "Ad load", sortable: false, w: 120, render: r => <AdLoadBar tier={r}/> },
              { key: "summary", label: "Ad load summary", sortable: false, render: r => (
                <span style={{ fontSize: 12.5, color: "var(--ink-2)" }}>{adTierSummary(r)}</span>) },
              { key: "status", label: "Status", render: r => <StatusPill status={r.status}/> },
              { key: "_e", label: "", sortable: false, align: "right", render: r => <Btn kind="ghost" size="sm" icon={<IconPencil size={13}/>} onClick={() => { setEditing(r); setDrawer(true); }}>Edit</Btn> },
            ]}/>
        </Card>
      )}

      <TierDrawer open={drawer} onClose={() => setDrawer(false)} tier={editing}
        existingCodes={data.adTiers.map(t => t.tier_code)} onSave={save}/>
    </Screen>
  );
};

// =================================================================
// §7b Ad defaults by access class
// =================================================================
const CLASSES = [
  { key: "anonymous_tier_code", label: "Anonymous / non-entitled" },
  { key: "paid_tier_code", label: "Paid (single purchase)" },
  { key: "day_pass_tier_code", label: "Day pass" },
  { key: "courtesy_tier_code", label: "Courtesy share" },
  { key: "subscription_tier_code", label: "Subscription" },
];

const AdDefaultsScreen = ({ data, setData }) => {
  const [cfg, setCfg] = React.useState(data.adConfig);
  React.useEffect(() => { setCfg(data.adConfig); }, [data.adConfig]);
  const dirty = JSON.stringify(cfg) !== JSON.stringify(data.adConfig);
  const tierByCode = (code) => data.adTiers.find(t => t.tier_code === code);
  const opts = data.adTiers.map(t => ({ label: `${t.name}${t.status === "retired" ? " (retired)" : ""}`, value: t.tier_code }));

  const save = () => { window.__api.write("putAdConfig", d => ({ ...d, adConfig: cfg }), { body: cfg, ok: "Ad defaults saved", err: "Couldn’t save defaults" }); };

  return (
    <Screen>
      <PageHead icon={IconSliders} title="Ad Defaults" subtitle="Tier applied to each access class when a purchase or plan doesn't specify its own."
        actions={<Btn kind="primary" disabled={!dirty} onClick={save}>Save defaults</Btn>}/>

      <Card>
        {CLASSES.map(c => {
          const tier = tierByCode(cfg[c.key]);
          const retired = tier && tier.status === "retired";
          const missing = cfg[c.key] && !tier;
          return (
            <FieldRow key={c.key} label={c.label} hint={tier ? adTierSummary(tier) : "No tier selected."}>
              <Select value={cfg[c.key] || ""} onChange={(v) => setCfg(prev => ({ ...prev, [c.key]: v }))} options={opts}/>
              {(retired || missing) && (
                <div style={{ marginTop: 8 }}>
                  <InlineHelp tone="warn" icon={<IconInfo size={13}/>}>
                    {missing ? "This tier no longer exists in the catalog." : "This default points at a retired tier."}
                  </InlineHelp>
                </div>
              )}
            </FieldRow>
          );
        })}
        <div style={{ paddingTop: 16 }}>
          <Banner tone="info" icon={<IconInfo size={15}/>}>
            The lowest rank a reader qualifies for is what applies (least-ads-wins). Plan-level ad tiers override these class defaults.
          </Banner>
        </div>
      </Card>
    </Screen>
  );
};

// =================================================================
// §7c Effective-policy debugger
// =================================================================
const AdDebuggerScreen = ({ data }) => {
  const [contentId, setContentId] = React.useState("article-4821");
  const [klass, setKlass] = React.useState("anonymous_tier_code");
  const [hasSub, setHasSub] = React.useState(false);
  const [result, setResult] = React.useState(null);

  const tierByCode = (code) => data.adTiers.find(t => t.tier_code === code);

  const run = () => {
    // Fire the real decision endpoint (logged; used directly in live mode).
    window.__api.read("decision", { query: { content_id: contentId, access_class: klass, has_subscription: hasSub } }).catch(() => {});
    const classTier = tierByCode(data.adConfig[klass]);
    const subTier = hasSub ? tierByCode(data.adConfig.subscription_tier_code) : null;
    // least-ads-wins → lowest rank among applicable tiers
    const applicable = [classTier, subTier].filter(Boolean);
    const effective = applicable.sort((a, b) => a.rank - b.rank)[0] || classTier;
    const entitled = klass !== "anonymous_tier_code" || hasSub;
    const reason = hasSub && effective === subTier
      ? "Active subscription resolves to the lowest-rank (fewest-ads) tier."
      : entitled ? `Access class “${CLASSES.find(c => c.key === klass).label}” default applies.`
      : "No entitlement — anonymous default applies; content is paywalled.";
    setResult({
      decision: entitled ? "entitled" : "paywalled",
      decision_reason: reason,
      ad_policy: effective,
      ads_policy: {
        ads: effective.max_ad_seconds === 0 ? "none" : "limited",
        max_seconds: effective.max_ad_seconds,
        max_count: effective.max_ads_per_session,
        formats: effective.allowed_formats,
      },
    });
  };

  return (
    <Screen>
      <PageHead icon={IconGauge} title="Policy Debugger" subtitle="Confirm how “what you paid for → which ads” resolves (least-ads-wins)." />

      <div className="pw-dbg">
        <Card title="Test input">
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <div><label className="pw-lbl">Content ID</label><Input value={contentId} onChange={e => setContentId(e.target.value)} placeholder="article-4821"/></div>
            <div><label className="pw-lbl">Access class</label>
              <Select value={klass} onChange={setKlass} options={CLASSES.map(c => ({ label: c.label, value: c.key }))}/></div>
            <Checkbox checked={hasSub} onChange={setHasSub} label="Reader also has an active subscription" description="Demonstrates least-ads-wins across entitlements."/>
            <Btn kind="primary" icon={<IconPlay size={15}/>} onClick={run} style={{ justifyContent: "center" }}>Resolve decision</Btn>
          </div>
        </Card>

        <Card title="Resolved policy">
          {!result ? (
            <EmptyState icon={<IconGauge size={28}/>} title="No decision yet" description="Run a test to see the resolved ad policy."/>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                <Pill tone={result.decision === "entitled" ? "good" : "warn"}>{result.decision}</Pill>
                <span style={{ fontSize: 12.5, color: "var(--muted)" }}>for <span style={{ fontFamily: "var(--mono)" }}>{contentId}</span></span>
              </div>
              <InlineHelp icon={<IconInfo size={13}/>}>{result.decision_reason}</InlineHelp>

              <div className="pw-dbg-policy">
                <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
                  <span style={{ fontWeight: 600 }}>{result.ad_policy.name} <span style={{ fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--muted)" }}>· rank {result.ad_policy.rank}</span></span>
                  <AdLoadBar tier={result.ad_policy} width={120}/>
                </div>
                <div style={{ fontSize: 13, color: "var(--ink-2)" }}>{adTierSummary(result.ad_policy)}</div>
              </div>

              <div>
                <div className="pw-lbl">Coarse ads_policy</div>
                <CodeBlock language="json">{JSON.stringify(result.ads_policy, null, 2)}</CodeBlock>
              </div>
            </div>
          )}
        </Card>
      </div>

      <style>{`
        .pw-dbg { display: grid; grid-template-columns: 1fr 1.3fr; gap: var(--gap); align-items: start; }
        .pw-dbg > * { min-width: 0; }
        @media (max-width: 1023px) { .pw-dbg { grid-template-columns: 1fr; } }
        .pw-dbg-policy { border: 1px solid var(--line); border-radius: var(--r-md); padding: 14px; background: var(--panel-2); }
      `}</style>
    </Screen>
  );
};

Object.assign(window, { AdTiersScreen, AdDefaultsScreen, AdDebuggerScreen, TierDrawer, NullableNumber });
