// =================================================================
// pw-screens-content.jsx — Publisher Admin · Content Pool
//
//   The publisher's own articles. They serve free in the recommendation
//   widget, filling slots paid demand did not, and can be boosted into
//   the paid auction where they compete on eCPM like any campaign.
// =================================================================

const CONTENT_TYPES = [
  { label: "Blog post", value: "blog", hint: "A standard article. Links straight through to the page." },
  { label: "Lead gen page", value: "lead_gen", hint: "Collects reader details. Pair it with a lead form CTA." },
  { label: "Listicle", value: "listicle", hint: "Ranked or numbered roundup. Tends to earn a higher CTR." },
];
const CONTENT_TYPE_LABEL = Object.fromEntries(CONTENT_TYPES.map(t => [t.value, t.label]));

const CTA_TYPES = [
  { label: "None", value: "none" },
  { label: "Link to a URL", value: "link" },
  { label: "Lead form", value: "lead_form" },
];

const LEAD_FIELD_TYPES = [
  { label: "Text", value: "text" },
  { label: "Email", value: "email" },
  { label: "Phone", value: "tel" },
  { label: "Long text", value: "textarea" },
  { label: "Dropdown", value: "select" },
  { label: "Checkbox", value: "checkbox" },
];

const blankItem = () => ({
  content_id: "",
  content_type: "blog",
  title: "",
  description: "",
  thumbnail_url: "",
  target_url: "",
  section: "",
  cta_type: "none",
  cta_label: "",
  cta_url: "",
  lead_form: [],
  status: "active",
});

// Field keys are how submitted answers are stored, so they have to match the API's
// lowercase/underscore rule. Derive one from the label rather than making publishers think about it.
const keyFromLabel = (label) =>
  String(label || "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 60);

// ---------------------------------------------------------------- thumbnail

const ThumbnailPicker = ({ value, onChange, onError }) => {
  const [busy, setBusy] = React.useState(false);
  const inputRef = React.useRef(null);

  const pick = async (file) => {
    if (!file) return;
    if (!["image/png", "image/jpeg", "image/gif"].includes(file.type)) {
      onError("Thumbnails must be PNG, JPEG or GIF.");
      return;
    }
    setBusy(true);
    try {
      const form = new FormData();
      form.append("file", file);
      const res = await window.__api.call("uploadContentThumb", { body: form });
      onChange(res.thumbnail_url);
    } catch (e) {
      onError(e.message || "Upload failed.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="pw-thumb">
      <div className={`pw-thumb-frame ${value ? "" : "empty"}`}>
        {value ? <img src={value} alt=""/> : <IconList size={26}/>}
      </div>
      <div className="pw-thumb-actions">
        <input ref={inputRef} type="file" accept="image/png,image/jpeg,image/gif" style={{ display: "none" }}
          onChange={(e) => { pick(e.target.files?.[0]); e.target.value = ""; }}/>
        <Btn kind="secondary" size="sm" onClick={() => inputRef.current?.click()} disabled={busy}>
          {busy ? "Uploading…" : value ? "Replace" : "Upload"}
        </Btn>
        {value && <Btn kind="ghost" size="sm" onClick={() => onChange("")}>Remove</Btn>}
        <div className="pw-thumb-hint">PNG, JPEG or GIF. Animated GIFs are supported.</div>
      </div>
      <style>{`
        .pw-thumb { display: grid; grid-template-columns: 132px 1fr; gap: 14px; align-items: start; }
        .pw-thumb-frame { aspect-ratio: 16/9; border-radius: var(--r-md); border: 1px solid var(--line); background: var(--panel-2); overflow: hidden; display: flex; align-items: center; justify-content: center; color: var(--muted-2); }
        .pw-thumb-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
        .pw-thumb-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
        .pw-thumb-hint { flex-basis: 100%; font-size: 12px; color: var(--muted); }
      `}</style>
    </div>
  );
};

// ---------------------------------------------------------------- lead form builder

const LeadFormBuilder = ({ fields, onChange }) => {
  const update = (idx, patch) => onChange(fields.map((f, i) => (i === idx ? { ...f, ...patch } : f)));
  const remove = (idx) => onChange(fields.filter((_, i) => i !== idx));
  const move = (idx, dir) => {
    const next = fields.slice();
    const to = idx + dir;
    if (to < 0 || to >= next.length) return;
    [next[idx], next[to]] = [next[to], next[idx]];
    onChange(next);
  };
  const add = () => onChange([...fields, { key: "", label: "", type: "text", required: false }]);

  const duplicateKeys = fields
    .map(f => f.key)
    .filter((k, i, all) => k && all.indexOf(k) !== i);

  return (
    <div className="pw-lfb">
      {fields.length === 0 && (
        <div className="pw-lfb-empty">No fields yet. A lead form needs at least one.</div>
      )}
      {fields.map((f, idx) => (
        <div className="pw-lfb-row" key={idx}>
          <div className="pw-lfb-grid">
            <div>
              <label className="pw-lbl">Label</label>
              <Input value={f.label} placeholder="Work email"
                onChange={(e) => {
                  const label = e.target.value;
                  // Keep the key in step with the label until someone edits the key directly.
                  const autoKey = !f.key || f.key === keyFromLabel(f.label);
                  update(idx, autoKey ? { label, key: keyFromLabel(label) } : { label });
                }}/>
            </div>
            <div>
              <label className="pw-lbl">Field key</label>
              <Input value={f.key} placeholder="work_email"
                onChange={(e) => update(idx, { key: keyFromLabel(e.target.value) })}/>
            </div>
            <div>
              <label className="pw-lbl">Type</label>
              <Select value={f.type} options={LEAD_FIELD_TYPES} onChange={(v) => update(idx, { type: v })}/>
            </div>
            <div className="pw-lfb-req">
              <label className="pw-lbl">Required</label>
              <Toggle checked={!!f.required} onChange={(v) => update(idx, { required: v })} label="Required"/>
            </div>
          </div>
          {f.type === "select" && (
            <div>
              <label className="pw-lbl">Choices (one per line)</label>
              <Textarea rows={3} value={(f.options || []).join("\n")}
                onChange={(e) => update(idx, { options: e.target.value.split("\n").map(s => s.trim()).filter(Boolean) })}/>
            </div>
          )}
          <div className="pw-lfb-tools">
            <Btn kind="ghost" size="sm" onClick={() => move(idx, -1)} disabled={idx === 0}>↑</Btn>
            <Btn kind="ghost" size="sm" onClick={() => move(idx, 1)} disabled={idx === fields.length - 1}>↓</Btn>
            <Btn kind="ghost" size="sm" onClick={() => remove(idx)}>Remove</Btn>
          </div>
        </div>
      ))}
      {duplicateKeys.length > 0 && (
        <div className="pw-lfb-warn">Duplicate field keys: {[...new Set(duplicateKeys)].join(", ")}. Each key must be unique.</div>
      )}
      <Btn kind="secondary" size="sm" onClick={add}>Add field</Btn>
      <style>{`
        .pw-lfb { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
        .pw-lfb-empty { font-size: 13px; color: var(--muted); }
        .pw-lfb-row { width: 100%; display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel-2); }
        .pw-lfb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: end; }
        .pw-lfb-req { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
        .pw-lfb-tools { display: flex; gap: 6px; }
        .pw-lfb-warn { font-size: 12.5px; color: var(--bad); }
      `}</style>
    </div>
  );
};

// ---------------------------------------------------------------- editor

const ContentEditor = ({ open, initial, onClose, onSaved, notify }) => {
  const [f, setF] = React.useState(blankItem());
  const [saving, setSaving] = React.useState(false);
  const set = (patch) => setF(prev => ({ ...prev, ...patch }));

  React.useEffect(() => {
    if (!open) return;
    setF(initial ? { ...blankItem(), ...initial, description: initial.description || "", section: initial.section || "" } : blankItem());
  }, [open, initial]);

  const isLeadForm = f.cta_type === "lead_form";
  const problems = [];
  if (!f.content_id.trim()) problems.push("Content ID is required.");
  if (!f.title.trim()) problems.push("Title is required.");
  if (!f.target_url.trim()) problems.push("Target URL is required.");
  if (f.cta_type === "link" && !f.cta_url.trim()) problems.push("A link CTA needs a destination URL.");
  if (isLeadForm && f.lead_form.length === 0) problems.push("A lead form CTA needs at least one field.");
  if (isLeadForm && f.lead_form.some(x => !x.key || !x.label)) problems.push("Every lead form field needs a label and a key.");

  const save = async () => {
    setSaving(true);
    try {
      const body = {
        content_id: f.content_id.trim(),
        content_type: f.content_type,
        title: f.title.trim(),
        description: f.description?.trim() || null,
        thumbnail_url: f.thumbnail_url || null,
        target_url: f.target_url.trim(),
        section: f.section?.trim() || null,
        cta_type: f.cta_type,
        cta_label: f.cta_label?.trim() || null,
        cta_url: f.cta_type === "link" ? f.cta_url.trim() : null,
        lead_form: isLeadForm ? f.lead_form : [],
        status: f.status,
      };
      await window.__api.write("saveContentItem", null, { body });
      notify("Content saved.");
      onSaved();
      onClose();
    } catch (e) {
      notify(e.message || "Save failed.", "bad");
    } finally {
      setSaving(false);
    }
  };

  return (
    <Drawer open={open} onClose={onClose} width={620}
      title={initial ? "Edit content" : "Add content"}
      footer={
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", alignItems: "center" }}>
          {problems.length > 0 && <span style={{ fontSize: 12.5, color: "var(--muted)", marginRight: "auto" }}>{problems[0]}</span>}
          <Btn kind="ghost" onClick={onClose}>Cancel</Btn>
          <Btn onClick={save} disabled={saving || problems.length > 0}>{saving ? "Saving…" : "Save"}</Btn>
        </div>
      }>
      <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
        <div>
          <label className="pw-lbl">Type</label>
          <Select value={f.content_type} options={CONTENT_TYPES} onChange={(v) => set({ content_type: v })}/>
          <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 5 }}>
            {CONTENT_TYPES.find(t => t.value === f.content_type)?.hint}
          </div>
        </div>

        <div>
          <label className="pw-lbl">Thumbnail</label>
          <ThumbnailPicker value={f.thumbnail_url} onChange={(v) => set({ thumbnail_url: v })}
            onError={(m) => notify(m, "bad")}/>
        </div>

        <div>
          <label className="pw-lbl">Title</label>
          <Input value={f.title} placeholder="How we cut latency in half" onChange={(e) => set({ title: e.target.value })}/>
        </div>

        <div>
          <label className="pw-lbl">Description</label>
          <Textarea rows={2} value={f.description} placeholder="One line that makes someone click."
            onChange={(e) => set({ description: e.target.value })}/>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div>
            <label className="pw-lbl">Content ID</label>
            <Input value={f.content_id} placeholder="article-1234" onChange={(e) => set({ content_id: e.target.value })}/>
            <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 5 }}>
              Must match the ID your site reports for this page — that is how view counts rank it.
            </div>
          </div>
          <div>
            <label className="pw-lbl">Section</label>
            <Input value={f.section} placeholder="Culture" onChange={(e) => set({ section: e.target.value })}/>
          </div>
        </div>

        <div>
          <label className="pw-lbl">Target URL</label>
          <Input value={f.target_url} placeholder="https://example.com/article" onChange={(e) => set({ target_url: e.target.value })}/>
        </div>

        <div style={{ borderTop: "1px solid var(--line-2)", paddingTop: 14 }}>
          <label className="pw-lbl">Call to action</label>
          <Select value={f.cta_type} options={CTA_TYPES} onChange={(v) => set({ cta_type: v })}/>
        </div>

        {f.cta_type !== "none" && (
          <div>
            <label className="pw-lbl">Button label</label>
            <Input value={f.cta_label} placeholder={isLeadForm ? "Get the report" : "Read more"}
              onChange={(e) => set({ cta_label: e.target.value })}/>
          </div>
        )}

        {f.cta_type === "link" && (
          <div>
            <label className="pw-lbl">CTA destination</label>
            <Input value={f.cta_url} placeholder="https://example.com/offer" onChange={(e) => set({ cta_url: e.target.value })}/>
          </div>
        )}

        {isLeadForm && (
          <div>
            <label className="pw-lbl">Lead form fields</label>
            <div style={{ fontSize: 12, color: "var(--muted)", margin: "2px 0 10px" }}>
              Submissions arrive in <strong>Leads</strong>. Only the fields declared here are stored.
            </div>
            <LeadFormBuilder fields={f.lead_form} onChange={(v) => set({ lead_form: v })}/>
          </div>
        )}

        <div style={{ display: "flex", alignItems: "center", gap: 10, borderTop: "1px solid var(--line-2)", paddingTop: 14 }}>
          <Toggle checked={f.status === "active"} onChange={(v) => set({ status: v ? "active" : "paused" })} label="Active"/>
          <span style={{ fontSize: 13 }}>{f.status === "active" ? "Serving in the widget" : "Paused — not served"}</span>
        </div>
      </div>
    </Drawer>
  );
};

// ---------------------------------------------------------------- boost

const BoostDialog = ({ open, item, onClose, onDone, notify }) => {
  const [f, setF] = React.useState({ pricing_model: "cpc", bid_amount: 0.4, daily_budget: 25, total_budget: "" });
  const [busy, setBusy] = React.useState(false);
  const set = (patch) => setF(prev => ({ ...prev, ...patch }));

  const boost = async () => {
    setBusy(true);
    try {
      await window.__api.write("boostContentItem", null, {
        params: { content_item_id: item.content_item_id },
        body: {
          pricing_model: f.pricing_model,
          bid_amount: Number(f.bid_amount),
          daily_budget: f.daily_budget === "" ? null : Number(f.daily_budget),
          total_budget: f.total_budget === "" ? null : Number(f.total_budget),
        },
      });
      notify("Boost started — this item now competes in the paid auction.");
      onDone();
      onClose();
    } catch (e) {
      notify(e.message || "Boost failed.", "bad");
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open={open} onClose={onClose} title={`Boost · ${item?.title || ""}`}>
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div style={{ fontSize: 13, color: "var(--muted)" }}>
          Boosting moves this item out of the free pool and into the paid auction, where it is ranked on
          effective CPM against every other campaign. You are charged for it like any advertiser.
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div>
            <label className="pw-lbl">Pricing</label>
            <Select value={f.pricing_model} onChange={(v) => set({ pricing_model: v })}
              options={[{ label: "CPC — per click", value: "cpc" }, { label: "CPM — per 1,000 impressions", value: "cpm" }]}/>
          </div>
          <div>
            <label className="pw-lbl">Bid</label>
            <Input type="number" step="0.01" min="0.01" value={f.bid_amount} prefix="$"
              onChange={(e) => set({ bid_amount: e.target.value })}/>
          </div>
          <div>
            <label className="pw-lbl">Daily budget</label>
            <Input type="number" step="1" min="1" value={f.daily_budget} prefix="$"
              onChange={(e) => set({ daily_budget: e.target.value })}/>
          </div>
          <div>
            <label className="pw-lbl">Total budget</label>
            <Input type="number" step="1" min="1" value={f.total_budget} prefix="$" placeholder="No cap"
              onChange={(e) => set({ total_budget: e.target.value })}/>
          </div>
        </div>
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
          <Btn kind="ghost" onClick={onClose}>Cancel</Btn>
          <Btn onClick={boost} disabled={busy || !(Number(f.bid_amount) > 0)}>{busy ? "Starting…" : "Start boost"}</Btn>
        </div>
      </div>
    </Modal>
  );
};

// ---------------------------------------------------------------- screen

const ContentPoolScreen = () => {
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [search, setSearch] = React.useState("");
  const [typeFilter, setTypeFilter] = React.useState("all");
  const [editing, setEditing] = React.useState(null);
  const [editorOpen, setEditorOpen] = React.useState(false);
  const [boosting, setBoosting] = React.useState(null);
  const [toast, setToast] = React.useState(null);

  const notify = (message, tone = "good") => {
    setToast({ message, tone });
    setTimeout(() => setToast(null), 4000);
  };

  const load = React.useCallback(async () => {
    setLoading(true);
    try {
      const res = await window.__api.read("listContentItems");
      setItems(res?.items || (Array.isArray(res) ? res : []));
    } catch (e) {
      notify(e.message || "Could not load content.", "bad");
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { load(); }, [load]);

  const remove = async (item) => {
    if (!window.confirm(`Remove “${item.title}” from the pool?`)) return;
    try {
      await window.__api.write("deleteContentItem", null, { params: { content_item_id: item.content_item_id } });
      notify("Content removed.");
      load();
    } catch (e) {
      notify(e.message || "Delete failed.", "bad");
    }
  };

  const unboost = async (item) => {
    try {
      await window.__api.write("unboostContentItem", null, { params: { content_item_id: item.content_item_id } });
      notify("Boost stopped — the item is back in the free pool.");
      load();
    } catch (e) {
      notify(e.message || "Could not stop the boost.", "bad");
    }
  };

  const filtered = items.filter(it => {
    if (typeFilter !== "all" && it.content_type !== typeFilter) return false;
    const q = search.trim().toLowerCase();
    if (!q) return true;
    return [it.title, it.content_id, it.section].some(v => String(v || "").toLowerCase().includes(q));
  });

  const boostedCount = items.filter(i => i.boost_campaign_id).length;
  const leadGenCount = items.filter(i => i.cta_type === "lead_form").length;

  return (
    <Screen>
      <PageHead icon={IconList} title="Content"
        subtitle="Your own articles, recirculated in the recommendation widget. They fill slots paid demand did not — and can be boosted into the paid auction."
        actions={<Btn icon={<IconPlus size={14}/>} onClick={() => { setEditing(null); setEditorOpen(true); }}>Add content</Btn>}/>

      <KpiGrid>
        <StatCard label="In pool" value={fmt.number(items.length)} sub="Articles available to recirculate"/>
        <StatCard label="Boosted" value={fmt.number(boostedCount)} sub="Competing in the paid auction"/>
        <StatCard label="Collecting leads" value={fmt.number(leadGenCount)} sub="Items with a lead form"/>
      </KpiGrid>

      <Card padded={false}>
        <div style={{ padding: "14px var(--pad)" }}>
          <Toolbar search={search} onSearchChange={setSearch} searchPlaceholder="Search title, ID or section">
            <Select value={typeFilter} onChange={setTypeFilter} style={{ minWidth: 170 }}
              options={[{ label: "All types", value: "all" }, ...CONTENT_TYPES.map(t => ({ label: t.label, value: t.value }))]}/>
          </Toolbar>
        </div>

        {loading ? (
          <div style={{ padding: 32, textAlign: "center", color: "var(--muted)" }}>Loading…</div>
        ) : filtered.length === 0 ? (
          <EmptyState icon={<IconList size={26}/>}
            title={items.length ? "Nothing matches that filter" : "No content yet"}
            description={items.length ? "Try a different search or type." : "Add your articles so the widget can recirculate them instead of rendering empty."}
            action={items.length ? undefined : <Btn onClick={() => { setEditing(null); setEditorOpen(true); }}>Add content</Btn>}/>
        ) : (
          <div className="pw-content-list">
            {filtered.map(item => (
              <div className="pw-content-row" key={item.content_item_id}>
                <div className={`pw-content-thumb ${item.thumbnail_url || item.image_url ? "" : "empty"}`}>
                  {item.thumbnail_url || item.image_url
                    ? <img src={item.thumbnail_url || item.image_url} alt=""/>
                    : <IconList size={20}/>}
                </div>
                <div className="pw-content-main">
                  <div className="pw-content-title">{item.title}</div>
                  <div className="pw-content-meta">
                    <Pill tone="neutral">{CONTENT_TYPE_LABEL[item.content_type] || item.content_type}</Pill>
                    {item.section && <span>{item.section}</span>}
                    <span className="pw-content-id">{item.content_id}</span>
                    {item.cta_type === "lead_form" && <Pill tone="info">Lead form · {item.lead_form?.length || 0} fields</Pill>}
                    {item.cta_type === "link" && <Pill tone="neutral">CTA link</Pill>}
                  </div>
                </div>
                <div className="pw-content-state">
                  {item.boost_campaign_id
                    ? <Pill tone="good">Boosted</Pill>
                    : <StatusPill status={item.status}/>}
                </div>
                <div className="pw-content-actions">
                  {item.boost_campaign_id
                    ? <Btn kind="secondary" size="sm" onClick={() => unboost(item)}>Stop boost</Btn>
                    : <Btn kind="secondary" size="sm" onClick={() => setBoosting(item)}>Boost</Btn>}
                  <Btn kind="ghost" size="sm" onClick={() => { setEditing(item); setEditorOpen(true); }}>Edit</Btn>
                  <Btn kind="ghost" size="sm" onClick={() => remove(item)}>Delete</Btn>
                </div>
              </div>
            ))}
          </div>
        )}
      </Card>

      <ContentEditor open={editorOpen} initial={editing} notify={notify}
        onClose={() => setEditorOpen(false)} onSaved={load}/>
      <BoostDialog open={!!boosting} item={boosting} notify={notify}
        onClose={() => setBoosting(null)} onDone={load}/>

      {toast && (
        <div className={`pw-toast ${toast.tone}`} role="status">{toast.message}</div>
      )}

      <style>{`
        .pw-content-list { display: flex; flex-direction: column; }
        .pw-content-row { display: grid; grid-template-columns: 84px 1fr auto auto; gap: 14px; align-items: center;
          padding: 12px var(--pad); border-top: 1px solid var(--line-2); }
        .pw-content-thumb { width: 84px; aspect-ratio: 16/9; border-radius: var(--r-sm); overflow: hidden; background: var(--panel-2);
          border: 1px solid var(--line); display: flex; align-items: center; justify-content: center; color: var(--muted-2); }
        .pw-content-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
        .pw-content-main { min-width: 0; }
        .pw-content-title { font-weight: 600; font-size: 14px; margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .pw-content-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 12px; color: var(--muted); }
        .pw-content-id { font-family: var(--mono); font-size: 11.5px; }
        .pw-content-actions { display: flex; gap: 6px; }
        .pw-toast { position: fixed; right: 20px; bottom: 20px; z-index: 120; padding: 11px 15px; border-radius: var(--r-md);
          background: var(--ink); color: white; font-size: 13px; box-shadow: var(--shadow-modal); max-width: 380px; }
        .pw-toast.bad { background: var(--bad); }
        @media (max-width: 820px) {
          .pw-content-row { grid-template-columns: 60px 1fr; }
          .pw-content-state, .pw-content-actions { grid-column: 2; }
        }
      `}</style>
    </Screen>
  );
};

Object.assign(window, { ContentPoolScreen, CONTENT_TYPES, CONTENT_TYPE_LABEL });
