/* eslint-disable */
const { useState, useEffect, useMemo, useRef } = React;

// ---------- API helpers ------------------------------------------------------

async function api(path, opts = {}) {
  const r = await fetch(path, { headers: { "content-type": "application/json" }, ...opts });
  const text = await r.text();
  let body = null;
  try { body = text ? JSON.parse(text) : null; } catch { body = { raw: text }; }
  if (!r.ok) throw Object.assign(new Error(body?.error || r.statusText), { status: r.status, body });
  return body;
}

async function uploadToR2(file, keyPrefix) {
  const safeName = file.name.replace(/[^A-Za-z0-9._-]/g, "_");
  const key = `${keyPrefix}/${Date.now()}_${safeName}`;
  const { url, public_url } = await api("/api/upload/presign", {
    method: "POST",
    body: JSON.stringify({ key, contentType: file.type || "application/octet-stream" }),
  });
  const put = await fetch(url, { method: "PUT", headers: { "content-type": file.type || "application/octet-stream" }, body: file });
  if (!put.ok) throw new Error(`R2 PUT failed: ${put.status} ${put.statusText}`);
  return { key, public_url };
}

const STEP_TONE = {
  prompt_drafted: "warn",
  prompt_approved: "info",
  ready_to_generate: "info",
  still_generated: "info",
  video_generated: "info",
  audio_generated: "info",
  images_generated: "info",
  delivered_to_slack: "accent",
  manually_posted: "ok",
  failed: "err",
  canceled: "ghost",
};
function relTime(iso) {
  if (!iso) return "—";
  const d = new Date(iso);
  const sec = Math.round((Date.now() - d.getTime()) / 1000);
  if (sec < 60) return `${sec}s`;
  if (sec < 3600) return `${Math.floor(sec/60)}m`;
  if (sec < 86400) return `${Math.floor(sec/3600)}h`;
  return `${Math.floor(sec/86400)}d`;
}
const fmtMoney = (n) => n == null ? "$0.00" : `$${Number(n).toFixed(2)}`;
const fmtClock = () => {
  const d = new Date();
  return `${String(d.getHours()).padStart(2,"0")}:${String(d.getMinutes()).padStart(2,"0")}:${String(d.getSeconds()).padStart(2,"0")}`;
};

// ---------- pod rail (top header) -------------------------------------------

function PodRail({ queue, costToday, alerts }) {
  const [clock, setClock] = useState(fmtClock());
  useEffect(() => { const id = setInterval(() => setClock(fmtClock()), 1000); return () => clearInterval(id); }, []);
  const liveCount = (queue?.in_progress || 0) + (queue?.ready_to_run || 0);
  return (
    <div className="pod-rail">
      <div className="brand">
        <div className="brand-mark">S</div>
        <div>
          <div className="brand-name">SHIYAA</div>
          <div className="brand-sub">AI INFLUENCER · v0.1</div>
        </div>
      </div>
      <div className={`cell ${liveCount ? "live" : ""}`}>
        <span className="dot pulse" style={{ color: liveCount ? "var(--accent)" : "var(--ok)" }} />
        <span className="label">Status</span>
        <span className="val">{liveCount ? `${liveCount} live` : "idle"}</span>
      </div>
      <div className="cell">
        <span className="label">Pending</span>
        <span className="val">{queue?.pending_approval ?? 0}</span>
      </div>
      <div className="cell">
        <span className="label">Hold 4h</span>
        <span className="val">{queue?.waiting_for_4h ?? 0}</span>
      </div>
      <div className="cell">
        <span className="label">Failed</span>
        <span className="val" style={{ color: queue?.failed ? "var(--err)" : undefined }}>{queue?.failed ?? 0}</span>
      </div>
      <div className="cell">
        <span className="label">Today</span>
        <span className="val tabular">{fmtMoney(costToday)}</span>
      </div>
      <div className="grow"></div>
      <div className="cell">
        {alerts > 0
          ? <span className="chip err">⚠ {alerts} alert{alerts === 1 ? "" : "s"}</span>
          : <span className="chip ok"><span className="dot" /> ALL CLEAR</span>}
      </div>
      <div className="timecode">
        <span className="label">IST</span>
        <span className="tabular">{clock}</span>
      </div>
    </div>
  );
}

// ---------- sidebar ---------------------------------------------------------

function Sidebar({ influencers, selectedId, onSelect, queue }) {
  return (
    <aside className="sidebar">
      <div className="section">
        <span>Personas</span>
        <span>{influencers.length}</span>
      </div>
      <div className="scroll">
        {influencers.length === 0 && (
          <div style={{ padding: "8px 14px", color: "var(--fg-4)", fontSize: 12 }}>
            none yet — create one in main panel
          </div>
        )}
        {influencers.map(i => (
          <button key={i.id} className={`item ${selectedId === i.id ? "active" : ""}`} onClick={() => onSelect(i.id)}>
            <span className="sw" />
            <span className="name">{i.name}</span>
            <span className="count">{i.language}</span>
          </button>
        ))}
      </div>

      <div className="section" style={{ borderTop: "1px solid var(--line-1)" }}>
        <span>Queue</span>
      </div>
      <div className="item" style={{ cursor: "default" }}>
        <span className="sw" style={{ background: "var(--warn)" }} />
        <span className="name">Pending approval</span>
        <span className="count">{queue?.pending_approval ?? 0}</span>
      </div>
      <div className="item" style={{ cursor: "default" }}>
        <span className="sw" style={{ background: "var(--info)" }} />
        <span className="name">Waiting 4h</span>
        <span className="count">{queue?.waiting_for_4h ?? 0}</span>
      </div>
      <div className="item" style={{ cursor: "default" }}>
        <span className="sw" style={{ background: "var(--accent)" }} />
        <span className="name">Ready to run</span>
        <span className="count">{queue?.ready_to_run ?? 0}</span>
      </div>
      <div className="item" style={{ cursor: "default" }}>
        <span className="sw" style={{ background: "var(--accent-bright)" }} />
        <span className="name">Delivered</span>
        <span className="count">{queue?.delivered ?? 0}</span>
      </div>
      <div className="item" style={{ cursor: "default" }}>
        <span className="sw" style={{ background: "var(--ok)" }} />
        <span className="name">Posted</span>
        <span className="count">{queue?.posted ?? 0}</span>
      </div>
      <div className="item" style={{ cursor: "default" }}>
        <span className="sw" style={{ background: "var(--err)" }} />
        <span className="name">Failed</span>
        <span className="count" style={{ color: queue?.failed ? "var(--err)" : undefined }}>{queue?.failed ?? 0}</span>
      </div>

      <div className="section" style={{ borderTop: "1px solid var(--line-1)", marginTop: 8 }}>
        <span>External</span>
      </div>
      <a className="item" href="https://n8n.shiyaaa.in/" target="_blank" rel="noreferrer" style={{ display: "flex" }}><span className="sw" /><span className="name">n8n editor →</span></a>
      <a className="item" href="https://fal.ai/dashboard/usage" target="_blank" rel="noreferrer" style={{ display: "flex" }}><span className="sw" /><span className="name">fal.ai usage →</span></a>
      <a className="item" href="https://platform.openai.com/usage" target="_blank" rel="noreferrer" style={{ display: "flex" }}><span className="sw" /><span className="name">OpenAI usage →</span></a>
      <a className="item" href="https://dash.cloudflare.com/" target="_blank" rel="noreferrer" style={{ display: "flex" }}><span className="sw" /><span className="name">Cloudflare →</span></a>
    </aside>
  );
}

// ---------- KPI strip --------------------------------------------------------

function KpiStrip({ queue, costs }) {
  const today = (costs?.today || []).reduce((s, r) => s + (r.total || 0), 0);
  const week = (costs?.week || []).reduce((s, r) => s + (r.total || 0), 0);
  return (
    <div className="kpi-grid">
      <div className={`kpi ${queue?.pending_approval ? "warn" : ""}`}>
        <div className="k">PENDING APPROVAL</div>
        <div className="v">{queue?.pending_approval ?? 0}</div>
        <div className="h">awaiting Slack click</div>
      </div>
      <div className="kpi">
        <div className="k">HOLD · 4H</div>
        <div className="v">{queue?.waiting_for_4h ?? 0}</div>
        <div className="h">approved, gated</div>
      </div>
      <div className={`kpi ${queue?.ready_to_run ? "accent" : ""}`}>
        <div className="k">READY · TICK</div>
        <div className="v">{queue?.ready_to_run ?? 0}</div>
        <div className="h">5-min orchestrator</div>
      </div>
      <div className={`kpi ${queue?.in_progress ? "warn" : ""}`}>
        <div className="k">IN PROGRESS</div>
        <div className="v">{queue?.in_progress ?? 0}</div>
        <div className="h">claimed</div>
      </div>
      <div className="kpi accent">
        <div className="k">DELIVERED</div>
        <div className="v">{queue?.delivered ?? 0}</div>
        <div className="h">awaiting Mark Posted</div>
      </div>
      <div className={`kpi ${today > 5 ? "warn" : ""}`}>
        <div className="k">COST · TODAY</div>
        <div className="v mono tabular">{fmtMoney(today)}</div>
        <div className="h">7d {fmtMoney(week)}</div>
      </div>
      <div className={`kpi ${queue?.failed ? "err" : ""}`}>
        <div className="k">FAILED</div>
        <div className="v">{queue?.failed ?? 0}</div>
        <div className="h">resume from Slack</div>
      </div>
    </div>
  );
}

// ---------- influencer create ----------------------------------------------

function CreatePersonaCard({ onCreated }) {
  const [open, setOpen] = useState(false);
  const [busy, setBusy] = useState(false);
  const [step, setStep] = useState("idle");
  const [logLines, setLogLines] = useState([]);
  const [files, setFiles] = useState([]);
  const log = (line, lv = "info") => setLogLines(L => [...L, { ts: new Date().toLocaleTimeString(), line, lv }]);

  const [form, setForm] = useState({
    name: "", niche: "tamil traditional fashion", language: "ta",
    theme: "", tone: "warm, conversational, culturally rooted",
    target_audience: "Tamil women 22-35 in India",
    age_range_lower: 22, age_range_upper: 28,
    visual_identity: { hair: "long, dark brown, wavy", skin: "warm brown", wardrobe: "earthy traditional with modern accents", setting: "indoor warm tones, occasional outdoor daylight" }
  });
  const setF = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const setVI = (k, v) => setForm(f => ({ ...f, visual_identity: { ...f.visual_identity, [k]: v } }));

  async function submit(e) {
    e?.preventDefault();
    setBusy(true); setStep("create"); setLogLines([]);
    try {
      log("creating influencer row…");
      const { id } = await api("/api/influencer/create", { method: "POST", body: JSON.stringify(form) });
      log(`✓ influencer_id = ${id}`, "ok");

      if (files.length > 0) {
        if (files.length !== 6) throw new Error(`expected exactly 6 photos, got ${files.length}`);
        setStep("upload");
        const sorted = [...files].sort((a, b) => a.name.localeCompare(b.name));
        for (let i = 0; i < sorted.length; i++) {
          const f = sorted[i];
          const ext = (f.name.split(".").pop() || "png").toLowerCase();
          const slot = String(i + 1).padStart(2, "0");
          const key = `influencers/${id}/reference/${slot}.${ext}`;
          log(`[${i+1}/6] ${f.name} → ${key}`);
          const presign = await api("/api/upload/presign", { method: "POST", body: JSON.stringify({ key, contentType: f.type || "image/jpeg" }) });
          const put = await fetch(presign.url, { method: "PUT", headers: { "content-type": f.type || "image/jpeg" }, body: f });
          if (!put.ok) throw new Error(`R2 PUT slot ${slot} failed: ${put.status}`);
        }
        log("✓ 6 photos uploaded", "ok");
      } else {
        log("(skipping photo upload — operator will upload separately)", "warn");
      }

      setStep("init");
      log("calling /webhook/initialize-influencer…");
      const init = await api("/api/webhook/initialize-influencer", {
        method: "POST",
        body: JSON.stringify({ influencer_id: id, language: form.language, theme: form.theme }),
      });
      log("✓ initialize: " + JSON.stringify(init), "ok");
      setStep("done");
      onCreated && onCreated(id);
    } catch (e) {
      log("✗ " + (e.message || String(e)), "err");
      setStep("error");
    } finally { setBusy(false); }
  }

  return (
    <section className="card">
      <header className="card-hd">
        <div className="l">
          <span>CONJURE A PERSONA</span>
          <span className="card-sub">one-time per influencer · INSERT row → 6 photos to R2 → init webhook</span>
        </div>
        <div className="r">
          <button className="btn ghost sm" onClick={() => setOpen(o => !o)}>{open ? "HIDE" : "NEW PERSONA"}</button>
        </div>
      </header>
      {open && (
        <div className="card-bd">
          <form onSubmit={submit} className="col gap-3">
            <div className="field-row">
              <div className="field"><label className="label">Name</label><input className="input" value={form.name} onChange={e => setF("name", e.target.value)} placeholder="Maya" required /></div>
              <div className="field"><label className="label">Niche</label><input className="input" value={form.niche} onChange={e => setF("niche", e.target.value)} required /></div>
            </div>
            <div className="field"><label className="label">Theme · locked aesthetic</label><input className="input" value={form.theme} onChange={e => setF("theme", e.target.value)} placeholder="tamil traditional saree fashion with modern fusion" required /></div>
            <div className="field-row three">
              <div className="field"><label className="label">Language</label>
                <select className="select" value={form.language} onChange={e => setF("language", e.target.value)}>
                  <option value="ta">Tamil (ta)</option>
                  <option value="en">English (en)</option>
                  <option value="mixed">Mixed</option>
                </select>
              </div>
              <div className="field"><label className="label">Age · lower</label><input type="number" min="18" max="80" className="input" value={form.age_range_lower} onChange={e => setF("age_range_lower", parseInt(e.target.value, 10))} /></div>
              <div className="field"><label className="label">Age · upper</label><input type="number" min="18" max="80" className="input" value={form.age_range_upper} onChange={e => setF("age_range_upper", parseInt(e.target.value, 10))} /></div>
            </div>
            <div className="field"><label className="label">Tone</label><input className="input" value={form.tone} onChange={e => setF("tone", e.target.value)} required /></div>
            <div className="field"><label className="label">Target audience</label><input className="input" value={form.target_audience} onChange={e => setF("target_audience", e.target.value)} required /></div>
            <div className="field-row">
              <div className="field"><label className="label">Hair</label><input className="input" value={form.visual_identity.hair} onChange={e => setVI("hair", e.target.value)} /></div>
              <div className="field"><label className="label">Skin</label><input className="input" value={form.visual_identity.skin} onChange={e => setVI("skin", e.target.value)} /></div>
            </div>
            <div className="field-row">
              <div className="field"><label className="label">Wardrobe</label><input className="input" value={form.visual_identity.wardrobe} onChange={e => setVI("wardrobe", e.target.value)} /></div>
              <div className="field"><label className="label">Setting</label><input className="input" value={form.visual_identity.setting} onChange={e => setVI("setting", e.target.value)} /></div>
            </div>
            <div className="field">
              <label className="label">6 reference photos · png / jpg · sorted by filename</label>
              <label className="file-input">
                <input type="file" multiple accept="image/png,image/jpeg" onChange={e => setFiles([...e.target.files])} />
                <span className="pick">CHOOSE FILES</span>
                <span>{files.length === 0 ? "no files" : `${files.length} file${files.length===1?"":"s"} — ${files.length === 6 ? "ready" : "need exactly 6"}`}</span>
              </label>
            </div>
            <div className="row between" style={{ marginTop: 4 }}>
              <button type="submit" className="btn primary lg" disabled={busy}>{busy ? "WORKING…" : "CREATE · UPLOAD · INITIALIZE"}</button>
              {step === "done" && <span className="chip ok">DONE</span>}
              {step === "error" && <span className="chip err">FAILED — see log</span>}
            </div>
            {logLines.length > 0 && (
              <div className="log">
                {logLines.map((l, i) => <div key={i}><span className="ts">{l.ts}</span><span className={`lv-${l.lv}`}>{l.line}</span></div>)}
              </div>
            )}
          </form>
        </div>
      )}
    </section>
  );
}

// ---------- influencer grid -------------------------------------------------

function InfluencersGrid({ influencers, selectedId, onSelect }) {
  if (!influencers.length) {
    return <p style={{ color: "var(--fg-3)", fontSize: 12 }}>no personas yet — conjure one above</p>;
  }
  return (
    <div className="persona-grid">
      {influencers.map(i => (
        <button key={i.id} className={`persona-card ${selectedId === i.id ? "active" : ""}`} onClick={() => onSelect(i.id)}>
          <div className="persona-art">
            <span className={`badge chip ${i.language === "ta" ? "accent" : i.language === "en" ? "info" : ""}`}>{(i.language || "?").toUpperCase()}</span>
          </div>
          <div className="persona-meta">
            <div className="name">{i.name}</div>
            <div className="handle">{i.niche || "—"}</div>
          </div>
          <div className="persona-stats">
            <div><div className="k">REFS</div><div className="v">{i.ref_count || 0} / 6</div></div>
            <div><div className="k">POSTS</div><div className="v">{i.post_count || 0}</div></div>
            <div><div className="k">ID</div><div className="v" style={{ fontSize: 10 }}>{(i.id || "").slice(0, 6)}</div></div>
          </div>
        </button>
      ))}
    </div>
  );
}

// ---------- schedule video --------------------------------------------------

function ScheduleVideoCard({ influencers, selectedId, onSelect, onScheduled }) {
  const [mode, setMode] = useState("i2v");
  const [voice, setVoice] = useState(false);
  const [duration, setDuration] = useState(5);
  const [idea, setIdea] = useState("");
  const [refVideoFile, setRefVideoFile] = useState(null);
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);
  const selected = influencers.find(i => i.id === selectedId);

  async function submit(e) {
    e?.preventDefault();
    setError(null); setResult(null);
    if (!selectedId) { setError("pick an influencer in the grid above"); return; }
    setBusy(true);
    try {
      let reference_video_url = undefined;
      if (mode === "v2v") {
        if (!refVideoFile) throw new Error("v2v selected — upload a source video");
        const { public_url } = await uploadToR2(refVideoFile, `influencers/${selectedId}/source-videos`);
        reference_video_url = public_url;
      }
      const body = { influencer_id: selectedId, voice, duration: Number(duration), idea: idea || undefined };
      if (reference_video_url) body.reference_video_url = reference_video_url;
      const r = await api("/api/webhook/schedule-video-post", { method: "POST", body: JSON.stringify(body) });
      setResult(r);
      onScheduled && onScheduled();
    } catch (e) {
      setError(e.message || String(e));
    } finally { setBusy(false); }
  }

  return (
    <section className="card">
      <header className="card-hd">
        <div className="l">
          <span>SCHEDULE A VIDEO</span>
          <span className="card-sub">{selected ? `· ${selected.name}` : "· no persona selected"}</span>
        </div>
      </header>
      <div className="card-bd">
        <form onSubmit={submit} className="col gap-3">
          <div className="field-row">
            <div className="field">
              <label className="label">Persona</label>
              <select className="select" value={selectedId || ""} onChange={e => onSelect(e.target.value)} required>
                <option value="" disabled>Select…</option>
                {influencers.map(i => <option key={i.id} value={i.id}>{i.name} · {i.language}</option>)}
              </select>
            </div>
            <div className="field">
              <label className="label">Mode</label>
              <div className="seg" style={{ width: "100%" }}>
                <button type="button" className={mode === "i2v" ? "on" : ""} onClick={() => setMode("i2v")} style={{ flex: 1 }}>I2V · SEEDANCE</button>
                <button type="button" className={mode === "v2v" ? "on" : ""} onClick={() => setMode("v2v")} style={{ flex: 1 }}>V2V · LUMA</button>
              </div>
            </div>
          </div>
          <div className="field-row">
            <div className="field">
              <label className="label">Duration · seconds (1–60)</label>
              <input type="number" min="1" max="60" className="input tabular" value={duration} onChange={e => setDuration(e.target.value)} />
            </div>
            <div className="field">
              <label className="label">Voice</label>
              <div className="seg" style={{ width: "100%" }}>
                <button type="button" className={!voice ? "on" : ""} onClick={() => setVoice(false)} style={{ flex: 1 }}>SILENT</button>
                <button type="button" className={voice ? "on" : ""} onClick={() => setVoice(true)} style={{ flex: 1 }}>VOICED</button>
              </div>
            </div>
          </div>
          <div className="field">
            <label className="label">Idea · seed for OpenAI</label>
            <textarea className="textarea" rows={3} value={idea} onChange={e => setIdea(e.target.value)} placeholder="morning saree routine, golden hour, looking out a window" />
          </div>
          {mode === "v2v" && (
            <div className="field">
              <label className="label">Source video · uploaded to R2 first</label>
              <label className="file-input">
                <input type="file" accept="video/mp4,video/quicktime,video/webm" onChange={e => setRefVideoFile(e.target.files?.[0] || null)} />
                <span className="pick">CHOOSE VIDEO</span>
                <span>{refVideoFile ? `${refVideoFile.name} · ${(refVideoFile.size / 1e6).toFixed(2)} MB` : "no file"}</span>
              </label>
            </div>
          )}
          <button type="submit" className="launch" disabled={busy}>
            <div className="top">{busy ? "SCHEDULING" : "DISPATCH"}</div>
            <div className="big">{busy ? "working…" : "schedule the brief"}</div>
            <div className="bot">
              <span>4-HOUR HOLD · OR ⚡ GENERATE NOW IN SLACK</span>
              <span>↵</span>
            </div>
            <div className="spark">↗</div>
          </button>
          {error && <div className="respbox err">{error}</div>}
          {result && <div className="respbox ok">{JSON.stringify(result, null, 2)}</div>}
        </form>
      </div>
    </section>
  );
}

// ---------- schedule image --------------------------------------------------

function ScheduleImageCard({ influencers, selectedId, onSelect, onScheduled }) {
  const [count, setCount] = useState(6);
  const [idea, setIdea] = useState("");
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);
  const selected = influencers.find(i => i.id === selectedId);

  async function submit(e) {
    e?.preventDefault();
    setError(null); setResult(null);
    if (!selectedId) { setError("pick an influencer first"); return; }
    setBusy(true);
    try {
      const r = await api("/api/webhook/schedule-image-post", {
        method: "POST",
        body: JSON.stringify({ influencer_id: selectedId, count: Number(count), idea: idea || undefined }),
      });
      setResult(r);
      onScheduled && onScheduled();
    } catch (e) {
      setError(e.message || String(e));
    } finally { setBusy(false); }
  }

  return (
    <section className="card">
      <header className="card-hd">
        <div className="l">
          <span>SCHEDULE AN IMAGE SET</span>
          <span className="card-sub">{selected ? `· ${selected.name}` : "· no persona selected"}</span>
        </div>
      </header>
      <div className="card-bd">
        <form onSubmit={submit} className="col gap-3">
          <div className="field-row">
            <div className="field">
              <label className="label">Persona</label>
              <select className="select" value={selectedId || ""} onChange={e => onSelect(e.target.value)} required>
                <option value="" disabled>Select…</option>
                {influencers.map(i => <option key={i.id} value={i.id}>{i.name} · {i.language}</option>)}
              </select>
            </div>
            <div className="field">
              <label className="label">Count · stills (1–20)</label>
              <input type="number" min="1" max="20" className="input tabular" value={count} onChange={e => setCount(e.target.value)} />
            </div>
          </div>
          <div className="field">
            <label className="label">Idea · same scene, different angles</label>
            <textarea className="textarea" rows={3} value={idea} onChange={e => setIdea(e.target.value)} placeholder="lookbook in a courtyard, mid-morning light" />
          </div>
          <button type="submit" className="btn primary lg" disabled={busy}>{busy ? "SCHEDULING…" : "SCHEDULE · POST TO SLACK"}</button>
          {error && <div className="respbox err">{error}</div>}
          {result && <div className="respbox ok">{JSON.stringify(result, null, 2)}</div>}
        </form>
      </div>
    </section>
  );
}

// ---------- activity table --------------------------------------------------

function ActivityCard({ posts, onResume, refresh }) {
  return (
    <section className="card">
      <header className="card-hd">
        <div className="l"><span>ACTIVITY</span><span className="card-sub">last 30 posts · auto-refresh 30s</span></div>
        <div className="r"><button className="btn ghost sm" onClick={refresh}>↻ REFRESH</button></div>
      </header>
      <div className="card-bd nopad">
        <div className="activity-head">
          <span></span>
          <span>POST</span>
          <span>STEP</span>
          <span>OUTPUT</span>
          <span>WHEN</span>
        </div>
        {(posts || []).slice(0, 30).map(p => (
          <div key={p.id} className={`queue-row ${p.step === "failed" ? "failed" : ""} ${p.state === "in_progress" ? "active" : ""}`}>
            <span className="dot" style={{ color: `var(--${STEP_TONE[p.step] === "ghost" ? "fg-3" : STEP_TONE[p.step] || "fg-3"})` }} />
            <div className="col" style={{ minWidth: 0 }}>
              <span className="title">{p.persona} · {p.kind === "video" ? (p.reference_video_url ? "v2v" : "i2v") : "image"} · {p.kind === "video" ? `${p.duration_sec || 5}s` : `${p.image_count || 6}×`}{p.voice_flag ? " · voiced" : ""}</span>
              <span className="meta" style={{ marginTop: 2, textTransform: "none", letterSpacing: 0, color: "var(--fg-3)", fontFamily: "var(--f-ui)", fontSize: 11 }}>{p.idea || "—"}</span>
            </div>
            <span className={`chip ${STEP_TONE[p.step] || "ghost"}`}>{p.step}</span>
            <div className="links">
              {p.video_url && <a href={p.video_url} target="_blank" rel="noreferrer">VIDEO</a>}
              {p.audio_url && <a href={p.audio_url} target="_blank" rel="noreferrer" style={{ color: "var(--warn)" }}>AUDIO</a>}
              {p.still_url && <a href={p.still_url} target="_blank" rel="noreferrer" style={{ color: "var(--info)" }}>STILL</a>}
              {!p.video_url && !p.audio_url && !p.still_url && <span style={{ color: "var(--fg-4)", fontFamily: "var(--f-mono)", fontSize: 10 }}>—</span>}
              {p.step === "failed" && <button onClick={() => onResume(p.id)} className="btn sm" style={{ marginLeft: 4 }}>RESUME</button>}
            </div>
            <span className="meta tabular">{relTime(p.created_at)}</span>
          </div>
        ))}
        {(!posts || posts.length === 0) && (
          <div style={{ padding: "24px", textAlign: "center", color: "var(--fg-4)", fontSize: 12 }}>
            no posts yet — schedule one above
          </div>
        )}
      </div>
    </section>
  );
}

// ---------- library: completed videos + image sets -------------------------

function Lightbox({ src, kind, meta, onClose }) {
  useEffect(() => {
    const onEsc = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onEsc);
    return () => document.removeEventListener("keydown", onEsc);
  }, [onClose]);
  return (
    <div className="lightbox" onClick={onClose}>
      <div className="frame" onClick={e => e.stopPropagation()}>
        {kind === "video"
          ? <video src={src} controls autoPlay playsInline />
          : <img src={src} alt="" />}
        <div className="meta">
          <span>{meta || ""}</span>
          <a href={src} target="_blank" rel="noreferrer">OPEN ↗</a>
        </div>
      </div>
    </div>
  );
}

function VideoTile({ v, onPreview }) {
  const fmt = (n) => `${n}s`;
  const isV2V = !!v.reference_video_url;
  return (
    <div className="video-tile">
      <div className="video-tile-art" onClick={() => onPreview({ src: v.video_url, kind: "video", meta: `${v.persona} · ${fmt(v.duration_sec || 5)} · ${isV2V ? "V2V" : "I2V"}` })}>
        {/* still preview if available, else show video poster lazily */}
        {v.still_url
          ? <img src={v.still_url} alt="" loading="lazy" />
          : <video src={v.video_url} preload="metadata" muted />}
        <span className="corner-tl">{(v.language || "?").toUpperCase()} · {isV2V ? "V2V" : "I2V"}</span>
        <span className="corner-tr">{fmt(v.duration_sec || 5)}{v.voice_flag ? " · ♪" : ""}</span>
        <span className="corner-bl">{relTime(v.created_at)}</span>
      </div>
      <div className="video-tile-meta">
        <div className="name">{v.persona}</div>
        <div className="idea">{v.idea || v.visual_prompt || "—"}</div>
        <div className="stats">
          <span>{v.step.replace(/_/g, " ")}</span>
          {v.audio_url && <span style={{ color: "var(--warn)" }}>· audio</span>}
        </div>
      </div>
      <div className="video-tile-actions">
        <a onClick={() => onPreview({ src: v.video_url, kind: "video", meta: `${v.persona}` })}>PREVIEW</a>
        <a href={v.video_url} target="_blank" rel="noreferrer">VIDEO ↗</a>
        {v.audio_url && <a href={v.audio_url} target="_blank" rel="noreferrer" style={{ color: "var(--warn)" }}>AUDIO ↗</a>}
      </div>
    </div>
  );
}

function ImageSetCard({ s, onPreview }) {
  const urls = Array.isArray(s.image_urls) ? s.image_urls : [];
  return (
    <div className="iset-card">
      <header className="iset-hd">
        <div className="l">
          <span className="name">{s.persona}</span>
          <span className="idea">{s.idea || s.visual_prompt || "—"}</span>
        </div>
        <div className="r">
          <span className="chip">{urls.length} IMG</span>
          <span className="chip ghost">{relTime(s.created_at)}</span>
          <span className={`chip ${s.step === "manually_posted" ? "ok" : "accent"}`}>{s.step.replace(/_/g, " ")}</span>
        </div>
      </header>
      <div className="iset-grid">
        {urls.map((u, i) => (
          <div key={i} className="iset-cell" onClick={() => onPreview({ src: u, kind: "image", meta: `${s.persona} · slot ${i + 1}/${urls.length}` })}>
            <img src={u} alt="" loading="lazy" />
            <span className="num">{String(i + 1).padStart(2, "0")}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function LibraryCard() {
  const [tab, setTab] = useState("videos"); // 'videos' | 'images'
  const [videos, setVideos] = useState(null);
  const [images, setImages] = useState(null);
  const [loading, setLoading] = useState(false);
  const [err, setErr] = useState(null);
  const [preview, setPreview] = useState(null);

  async function load() {
    setLoading(true); setErr(null);
    try {
      if (tab === "videos") {
        const r = await api("/api/library/videos?limit=30");
        setVideos(r.data || []);
      } else {
        const r = await api("/api/library/images?limit=20");
        setImages(r.data || []);
      }
    } catch (e) { setErr(e.message || String(e)); }
    finally { setLoading(false); }
  }
  useEffect(() => { load(); }, [tab]);

  return (
    <section className="card">
      <header className="card-hd">
        <div className="l">
          <span>LIBRARY · COMPLETED OUTPUTS</span>
          <span className="card-sub">delivered or manually posted</span>
        </div>
        <div className="r">
          <div className="lib-tabs">
            <button className={tab === "videos" ? "on" : ""} onClick={() => setTab("videos")}>VIDEOS{videos ? ` · ${videos.length}` : ""}</button>
            <button className={tab === "images" ? "on" : ""} onClick={() => setTab("images")}>IMAGE SETS{images ? ` · ${images.length}` : ""}</button>
          </div>
          <button className="btn ghost sm" onClick={load} disabled={loading}>{loading ? "…" : "↻"}</button>
        </div>
      </header>
      <div className="card-bd">
        {err && <div className="respbox err">{err}</div>}
        {tab === "videos" && (
          videos === null
            ? <div className="lib-empty">loading…</div>
            : videos.length === 0
              ? <div className="lib-empty">no completed videos yet — generate one and it'll show up here once Slack receives the URL</div>
              : <div className="video-grid">{videos.map(v => <VideoTile key={v.id} v={v} onPreview={setPreview} />)}</div>
        )}
        {tab === "images" && (
          images === null
            ? <div className="lib-empty">loading…</div>
            : images.length === 0
              ? <div className="lib-empty">no completed image sets yet</div>
              : <div>{images.map(s => <ImageSetCard key={s.id} s={s} onPreview={setPreview} />)}</div>
        )}
      </div>
      {preview && <Lightbox {...preview} onClose={() => setPreview(null)} />}
    </section>
  );
}

// ---------- ops panel (right rail) -----------------------------------------

function OpsPanel({ costs, executions, alerts }) {
  const week = costs?.week || [];
  const today = costs?.today || [];
  const totalWeek = week.reduce((s, r) => s + (r.total || 0), 0);
  const totalToday = today.reduce((s, r) => s + (r.total || 0), 0);

  return (
    <aside className="ops-panel">
      <div className="ops-section">
        <h3>COST · LAST 7D</h3>
        {week.length === 0 ? (
          <p style={{ color: "var(--fg-4)", fontSize: 11 }}>no spend recorded yet</p>
        ) : (
          week.map(r => {
            const pct = totalWeek > 0 ? (r.total / totalWeek) * 100 : 0;
            return (
              <div key={r.vendor} className="vendor-bar">
                <div className="vb-row">
                  <span className="v-name">{r.vendor}</span>
                  <span className="v-amt tabular">{fmtMoney(r.total)}</span>
                </div>
                <div className="vb-row">
                  <span className="v-meta">{r.calls} calls</span>
                  <span className="v-meta tabular">{pct.toFixed(0)}%</span>
                </div>
                <div className="vb-track"><div className="vb-fill" style={{ width: `${pct}%` }} /></div>
              </div>
            );
          })
        )}
        <hr className="hairline" style={{ margin: "10px 0" }} />
        <div className="row between" style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
          <span>TODAY</span><span className="tabular" style={{ color: "var(--fg-0)" }}>{fmtMoney(totalToday)}</span>
        </div>
        <div className="row between" style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: "0.1em", marginTop: 4 }}>
          <span>30D</span><span className="tabular" style={{ color: "var(--fg-0)" }}>{fmtMoney(costs?.month_total || 0)}</span>
        </div>
      </div>

      <div className="ops-section">
        <h3>RECENT EXECUTIONS</h3>
        {(executions || []).length === 0 ? (
          <p style={{ color: "var(--fg-4)", fontSize: 11 }}>no executions yet</p>
        ) : (
          <div className="col gap-2">
            {(executions || []).slice(0, 12).map(e => (
              <div key={e.id} className="row" style={{ fontSize: 11, padding: "4px 0", borderBottom: "1px solid var(--line-1)" }}>
                <span className={`chip ${e.status === "success" ? "ok" : e.status === "error" ? "err" : "info"}`} style={{ minWidth: 56, justifyContent: "center" }}>{e.status || "…"}</span>
                <span className="mono" style={{ color: "var(--fg-3)", fontSize: 10 }}>#{e.id}</span>
                <span style={{ marginLeft: "auto", color: "var(--fg-4)", fontFamily: "var(--f-mono)", fontSize: 10 }}>{relTime(e.startedAt)}</span>
              </div>
            ))}
          </div>
        )}
      </div>

      <div className="ops-section">
        <h3>ALERTS</h3>
        {(!alerts || alerts.length === 0) ? (
          <p style={{ color: "var(--fg-4)", fontSize: 11 }}>all clear ✓</p>
        ) : (
          <div className="col gap-2">
            {alerts.slice(0, 8).map(a => (
              <div key={a.id} style={{ padding: "8px 10px", border: "1px solid var(--line-1)", borderRadius: "var(--r-sm)" }}>
                <div className="row" style={{ marginBottom: 4 }}>
                  <span className={`chip ${a.severity === "critical" || a.severity === "error" ? "err" : a.severity === "warn" ? "warn" : "info"}`}>{a.severity}</span>
                  <span style={{ color: "var(--fg-3)", fontFamily: "var(--f-mono)", fontSize: 10, textTransform: "uppercase", letterSpacing: "0.1em" }}>{a.source}</span>
                  <span style={{ marginLeft: "auto", color: "var(--fg-4)", fontFamily: "var(--f-mono)", fontSize: 10 }}>{relTime(a.created_at)}</span>
                </div>
                <p style={{ color: "var(--fg-2)", fontSize: 11.5, margin: 0, lineHeight: 1.5 }}>{a.message}</p>
              </div>
            ))}
          </div>
        )}
      </div>

      <div className="ops-section" style={{ borderBottom: 0 }}>
        <h3>STACK</h3>
        <div className="col gap-2" style={{ fontFamily: "var(--f-mono)", fontSize: 10.5, color: "var(--fg-2)", lineHeight: 1.7 }}>
          <div>video · <span style={{ color: "var(--accent-bright)" }}>seedance 2.0 pro</span> on fal.ai</div>
          <div>v2v · <span style={{ color: "var(--accent-bright)" }}>luma ray 2 modify</span></div>
          <div>image · <span style={{ color: "var(--accent-bright)" }}>seedream 4.5 edit</span> · 6-ref</div>
          <div>voice · <span style={{ color: "var(--accent-bright)" }}>elevenlabs</span> (ta only)</div>
          <div>llm · <span style={{ color: "var(--accent-bright)" }}>openai gpt-4.1</span></div>
          <div>media · <span style={{ color: "var(--accent-bright)" }}>media.shiyaaa.in</span> (R2)</div>
        </div>
      </div>
    </aside>
  );
}

// ---------- root app --------------------------------------------------------

function App() {
  const [influencers, setInfluencers] = useState([]);
  const [selectedId, setSelectedId] = useState(null);
  const [posts, setPosts] = useState([]);
  const [costs, setCosts] = useState({});
  const [queue, setQueue] = useState({});
  const [executions, setExecutions] = useState([]);
  const [alerts, setAlerts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState(null);

  async function loadAll() {
    try {
      const [i, p, c, q, e, a] = await Promise.all([
        api("/api/influencers"), api("/api/posts?limit=50"),
        api("/api/cost-summary"), api("/api/queue-state"),
        api("/api/executions?limit=20"), api("/api/alerts"),
      ]);
      setInfluencers(i.data || []);
      setPosts(p.data || []);
      setCosts(c || {});
      setQueue(q || {});
      setExecutions(e.data || []);
      setAlerts(a.data || []);
      setErr(null);
    } catch (e) {
      setErr(e.message || String(e));
    } finally {
      setLoading(false);
    }
  }
  useEffect(() => { loadAll(); const id = setInterval(loadAll, 30000); return () => clearInterval(id); }, []);

  async function onResume(post_id) {
    try { await api("/api/webhook/resume-post", { method: "POST", body: JSON.stringify({ post_id }) }); loadAll(); }
    catch (e) { alert(e.message || String(e)); }
  }

  const todayTotal = (costs?.today || []).reduce((s, r) => s + (r.total || 0), 0);

  return (
    <div className="app">
      <PodRail queue={queue} costToday={todayTotal} alerts={alerts.length} />
      <Sidebar influencers={influencers} selectedId={selectedId} onSelect={setSelectedId} queue={queue} />
      <main className="main">
        <div className="main-inner">
          <div className="page-head">
            <div>
              <span className="crumb">CONSOLE · OVERVIEW</span>
              <h1>Conjure <em>personas</em>, dispatch <em>briefs</em>.</h1>
            </div>
            <div className="row gap-2">
              <span className="chip ghost">{influencers.length} PERSONAS</span>
              <span className="chip ghost">{posts.length} POSTS</span>
              <span className="chip">{loading ? "LOADING…" : "30S"}</span>
            </div>
          </div>

          {err && <div className="respbox err">API error: {err}</div>}

          <KpiStrip queue={queue} costs={costs} />

          <CreatePersonaCard onCreated={() => loadAll()} />

          <section className="card">
            <header className="card-hd">
              <div className="l"><span>INFLUENCERS</span><span className="card-sub">click a card to select for scheduling</span></div>
            </header>
            <div className="card-bd"><InfluencersGrid influencers={influencers} selectedId={selectedId} onSelect={setSelectedId} /></div>
          </section>

          <ScheduleVideoCard influencers={influencers} selectedId={selectedId} onSelect={setSelectedId} onScheduled={loadAll} />
          <ScheduleImageCard influencers={influencers} selectedId={selectedId} onSelect={setSelectedId} onScheduled={loadAll} />

          <ActivityCard posts={posts} onResume={onResume} refresh={loadAll} />

          <LibraryCard />

          <footer style={{ paddingTop: 8, color: "var(--fg-4)", fontFamily: "var(--f-mono)", fontSize: 10, textAlign: "center", letterSpacing: "0.1em", textTransform: "uppercase" }}>
            SHIYAA CONSOLE · n8n.shiyaaa.in · media.shiyaaa.in · console.shiyaaa.in
          </footer>
        </div>
      </main>
      <OpsPanel costs={costs} executions={executions} alerts={alerts} />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
