// socialmediamanager/public/assets/js/app.js
const API = {
    zapier: "../api/zapier.php",
  crud: "../api/crud.php",
  draft: "../api/chat_draft.php",
  tagx: "../api/tagx_run.php",
  publishNow: "../api/publish_now.php",
  publishDue: "../api/publish_due.php",
  bulk: "../api/bulk_import.php",
  router: "../api/router_run.php",
  status: "../api/status.php",
  share: "../api/sharepacks.php",
  jacai: "../api/jacai.php"
};
const TOOL_TYPES = [
  "builtin",
  "http",
  "tagx",
  "openai_chat",
  "open_ai",
  "oauth_post",
  "cron_post"
];
const el = (s)=>document.querySelector(s);
const els=(s)=>Array.from(document.querySelectorAll(s));

function esc(s){return (s??"").toString().replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#039;");}
function fmt(s){ if(!s) return ""; try{return new Date(s).toLocaleString();}catch(e){return s;} }
async function get(url){const r=await fetch(url,{cache:"no-store"});return await r.json();}
async function post(url, data) {
  const res = await fetch(url, {
    method: "POST",
    headers: {"Content-Type":"application/json"},
    body: JSON.stringify(data||{})
  });

  const text = await res.text();

  // If server returned 500, show the raw body in console (even if empty)
  if(!res.ok){
    console.error("API ERROR", res.status, res.statusText, "RAW:", text);
    // try JSON parse anyway if it IS json
    try{
      const j = JSON.parse(text || "{}");
      return j;
    }catch(e){
      throw new Error(`Server error ${res.status}. Raw response logged in console.`);
    }
  }

  try {
    return JSON.parse(text);
  } catch (e) {
    console.error("Server returned non-JSON (200 OK). RAW:", text);
    throw new Error("Server returned non-JSON. See console RAW response.");
  }
}
function showTab(name){
  els(".navitem, .navbtn").forEach(b=>b.classList.toggle("active", b.dataset.tab===name));
  els(".tab").forEach(t=>t.classList.remove("active"));
  el("#tab-"+name).classList.add("active");
}
els(".navitem, .navbtn").forEach(b=>b.addEventListener("click",()=>showTab(b.dataset.tab)));

const modal={root:el("#modal"),title:el("#modalTitle"),body:el("#modalBody"),close:el("#modalClose"),cancel:el("#modalCancel"),save:el("#modalSave"),del:el("#modalDelete"),onSave:null,onDelete:null};
function openModal({title,html,showDelete=false,onSave,onDelete}){
  modal.title.textContent=title; modal.body.innerHTML=html;
  modal.del.style.display=showDelete?"":"none";
  modal.onSave=onSave||null; modal.onDelete=onDelete||null;
  modal.root.classList.remove("hidden");
}
function closeModal(){ modal.root.classList.add("hidden"); modal.body.innerHTML=""; modal.onSave=null; modal.onDelete=null; }
modal.close.addEventListener("click",closeModal); modal.cancel.addEventListener("click",closeModal);
modal.root.addEventListener("click",(e)=>{if(e.target===modal.root) closeModal();});
modal.save.addEventListener("click",async()=>{ if(modal.onSave) await modal.onSave(); });
modal.del.addEventListener("click",async()=>{ if(modal.onDelete) await modal.onDelete(); });

let sitesCache=null;
async function getSites(){
  if(sitesCache) return sitesCache;
  const u=new URL(API.crud,location.href); u.searchParams.set("action","list_sites");
  const d=await get(u.toString()); sitesCache=d.ok?d.items:[]; return sitesCache;
}

async function listArticles({q="",status=""}={}){
  const u=new URL(API.crud,location.href);
  u.searchParams.set("action","list_articles");
  if(q) u.searchParams.set("q",q);
  if(status) u.searchParams.set("status",status);
  const d=await get(u.toString());
  return d.ok ? d.items : [];
}

async function loadDrafts(){
  const q=el("#draftSearch").value.trim();
  const status=el("#draftStatus").value.trim();
  const items = await listArticles({q,status});
  const list=el("#draftList");
  if(!items.length){ list.innerHTML=`<div class="card muted">No items found.</div>`; return; }

  list.innerHTML=items.map(a=>`
    <div class="item">
      <div style="flex:1;">
        <h3>${esc(a.title)}</h3>
        <div class="meta">
          <span class="pill">${esc(a.status)}</span>
          <span class="pill">slug: <code>${esc(a.slug)}</code></span>
          ${a.site_name?`<span class="pill">site: ${esc(a.site_name)}</span>`:""}
          ${a.publish_at?`<span class="pill">publish: ${esc(fmt(a.publish_at))}</span>`:""}
        </div>
        <div class="meta" style="margin-top:6px;">Updated: ${esc(fmt(a.updated_at))}</div>
      </div>
      <div style="display:flex;flex-direction:column;gap:8px;">
        <button class="btn ghost" data-edit-article="${a.id}">Edit</button>
<button class="btn ghost" data-tagx="${a.id}">TagX</button>
<button class="btn ghost" data-zapier="${a.id}">&#128640; Send to Zapier</button>
<button class="btn" data-publish="${a.id}">Publish</button>
      </div>
    </div>
  `).join("");

  els("[data-edit-article]").forEach(b=>b.addEventListener("click",()=>editArticle(b.dataset.editArticle)));
  els("[data-tagx]").forEach(b=>b.addEventListener("click",()=>runTagx(b.dataset.tagx)));
  els("[data-publish]").forEach(b=>b.addEventListener("click",()=>publishNow(b.dataset.publish)));
 els("[data-zapier]").forEach(b =>
  b.addEventListener("click", () => sendToZapier(b.dataset.zapier))
);
}

function toast(msg){
  // simple, safe fallback (no dependencies)
  console.log("[toast]", msg);
  alert(msg);
}

async function loadQueue(){
  const items = await listArticles({status:"queued"});
  const list=el("#queueList");
  if(!items.length){ list.innerHTML=`<div class="card muted">No queued items.</div>`; return; }

  list.innerHTML=items.map(a=>`
    <div class="item">
      <div style="flex:1;">
        <h3>${esc(a.title)}</h3>
        <div class="meta">
          <span class="pill">queued</span>
          <span class="pill">publish: ${esc(fmt(a.publish_at))}</span>
          ${a.site_name?`<span class="pill">site: ${esc(a.site_name)}</span>`:""}
        </div>
      </div>
      <div>
        <button class="btn ghost" data-edit-article="${a.id}">Edit</button>
      </div>
    </div>
  `).join("");
  els("[data-edit-article]").forEach(b=>b.addEventListener("click",()=>editArticle(b.dataset.editArticle)));
}

async function editArticle(id){
  const u=new URL(API.crud,location.href); u.searchParams.set("action","get_article"); u.searchParams.set("id",id);
  const d=await get(u.toString()); if(!d.ok){alert(d.error);return;}
  const a=d.item; const sites=await getSites();
  const opts=[`<option value="">(none)</option>`].concat(
    sites.map(s=>`<option value="${s.id}" ${String(s.id)===String(a.site_profile_id)?"selected":""}>${esc(s.name)}</option>`)
  ).join("");

  openModal({
    title:`Edit Article #${a.id}`, showDelete:true,
    html:`
      <label class="label">Title</label>
      <input class="input" id="m_title" value="${esc(a.title)}"/>

      <div class="grid">
        <div>
          <label class="label">Slug</label>
          <input class="input" id="m_slug" value="${esc(a.slug)}"/>
        </div>
        <div>
          <label class="label">Status</label>
          <select class="input" id="m_status">
            ${["draft","queued","published","archived"].map(st=>`<option value="${st}" ${st===a.status?"selected":""}>${st}</option>`).join("")}
          </select>
        </div>
      </div>

      <div class="grid">
        <div>
          <label class="label">Site</label>
          <select class="input" id="m_site">${opts}</select>
        </div>
        <div>
          <label class="label">Publish at (ISO; required for queued)</label>
          <input class="input" id="m_publish_at" value="${esc(a.publish_at||"")}" placeholder="2026-01-01T09:00:00-05:00"/>
        </div>
      </div>

      <label class="label">Body HTML</label>
      <textarea class="input textarea" id="m_body" rows="14">${esc(a.body_html)}</textarea>
    `,
    onSave: async ()=>{
      const payload={
        id:a.id,
        title:el("#m_title").value.trim(),
        slug:el("#m_slug").value.trim(),
        status:el("#m_status").value.trim(),
        site_profile_id:el("#m_site").value,
        publish_at:el("#m_publish_at").value.trim(),
        body_html:el("#m_body").value
      };
      const r=await post(API.crud+"?action=save_article",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    },
    onDelete: async ()=>{
      if(!confirm("Delete this article?")) return;
      const r=await post(API.crud+"?action=delete_article",{id:a.id});
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
}

el("#btnRefreshDrafts").addEventListener("click",loadDrafts);
el("#draftSearch").addEventListener("input",debounce(loadDrafts,250));
el("#draftStatus").addEventListener("change",loadDrafts);
el("#btnRefreshQueue").addEventListener("click",loadQueue);

el("#btnNewDraft").addEventListener("click",()=>{
  openModal({
    title:"New Draft",
    html:`
      <label class="label">Title</label>
      <input class="input" id="m_title" placeholder="My new post"/>
      <label class="label">Slug (optional)</label>
      <input class="input" id="m_slug" placeholder="auto-generated if blank"/>
      <label class="label">Body HTML</label>
      <textarea class="input textarea" id="m_body" rows="14" placeholder="<h1>Title</h1>..."></textarea>
    `,
    onSave: async ()=>{
      const payload={title:el("#m_title").value.trim(), slug:el("#m_slug").value.trim(), body_html:el("#m_body").value, status:"draft"};
      const r=await post(API.crud+"?action=save_article",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
});

// TagX
async function runTagx(articleId){
  const res = await post(API.tagx, {article_id:Number(articleId)});
  if(!res.ok){alert(res.error);return;}
  const r = res.result;
  openModal({
    title:`TagX Results (Article #${articleId})`,
    html:`
      <div class="card">
        <div class="meta">Meta Title</div>
        <div class="codeblock" id="t_mt">${esc(r.meta_title)}</div>
        <div class="row" style="margin-top:8px;">
          <button class="btn ghost" data-copy="#t_mt">Copy</button>
          <div class="spacer"></div>
        </div>
        <div class="meta" style="margin-top:10px;">Meta Description</div>
        <div class="codeblock" id="t_md">${esc(r.meta_description)}</div>
        <div class="row" style="margin-top:8px;">
          <button class="btn ghost" data-copy="#t_md">Copy</button>
          <div class="spacer"></div>
        </div>
        <div class="meta" style="margin-top:10px;">Hashtags</div>
        <div class="codeblock" id="t_ht">${esc((r.hashtags||[]).join(" "))}</div>
        <div class="row" style="margin-top:8px;">
          <button class="btn ghost" data-copy="#t_ht">Copy</button>
          <div class="spacer"></div>
        </div>
        <div class="meta" style="margin-top:10px;">Meta Snippet</div>
        <pre class="codeblock" id="t_sn">${esc(r.meta_snippet)}</pre>
        <div class="row" style="margin-top:8px;">
          <button class="btn ghost" data-copy="#t_sn">Copy</button>
          <div class="spacer"></div>
        </div>
      </div>
    `
  });
  els("[data-copy]").forEach(b=>b.addEventListener("click",()=>copyFrom(b.getAttribute("data-copy"))));
}
function copyFrom(selector){
  const node = document.querySelector(selector);
  const text = node?.innerText || node?.textContent || "";
  navigator.clipboard.writeText(text);
}

// Publisher
async function publishNow(articleId){
  if(!confirm("Publish this article now?")) return;
  const res = await post(API.publishNow, {article_id:Number(articleId)});
  if(!res.ok){alert(res.error);return;}
  alert("Published!\n" + (res.url||""));
  await refreshAll();
}

el("#btnPublishDue").addEventListener("click", async ()=>{
  el("#publishOutput").textContent = "Publishing due itemsâ€¦";
  const res = await post(API.publishDue, {});
  if(!res.ok){ el("#publishOutput").textContent = "Error: " + res.error; return; }
  el("#publishOutput").textContent = "Published count: " + (res.count ?? 0);
  await refreshAll();
});
async function sendToZapier(articleId){
  articleId = Number(articleId || 0);
  if(!articleId) { toast("Missing article id"); return; }

  // No DOM lookups here. Works from drafts list, editor modal, anywhere.
  const r = await post(API.zapier + "?action=send", { id: articleId });

  if(!r || !r.ok){
    console.error("Zapier send failed", r);
    toast("Zapier send failed: " + (r?.error || "unknown"));
    return;
  }

  toast("Sent to Zapier ✅ " + (r.request_id ? ("(" + r.request_id + ")") : ""));
}


// Bulk import
el("#btnBulkImport").addEventListener("click", async ()=>{
  const text = el("#bulkText").value;
  if(!text.trim()){ alert("Paste posts first."); return; }
  el("#bulkOutput").textContent = "Importingâ€¦";
  const res = await post(API.bulk, {text});
  if(!res.ok){ el("#bulkOutput").textContent = "Error: " + res.error; return; }
  el("#bulkOutput").textContent = "Imported " + (res.created?.length||0) + " drafts.";
  el("#bulkText").value = "";
  await refreshAll();
  showTab("drafts");
});

// Sites CRUD
async function loadSites(){
  sitesCache=null;
  const u=new URL(API.crud,location.href); u.searchParams.set("action","list_sites");
  const d=await get(u.toString()); const sites=d.ok?d.items:[];
  const list=el("#siteList");
  if(!sites.length){list.innerHTML=`<div class="card muted">No sites yet. Add one.</div>`; return;}
  list.innerHTML=sites.map(s=>`
    <div class="item">
      <div style="flex:1;">
        <h3>${esc(s.name)}</h3>
        <div class="meta"><span class="pill">base: <code>${esc(s.base_url)}</code></span></div>
        <div class="meta" style="margin-top:6px;">
          publish_dir: <code>${esc(s.publish_dir)}</code><br/>
          posts_index: <code>${esc(s.posts_index_path)}</code>
        </div>
      </div>
      <div><button class="btn ghost" data-edit-site="${s.id}">Edit</button></div>
    </div>
  `).join("");
  els("[data-edit-site]").forEach(b=>b.addEventListener("click",()=>editSite(b.dataset.editSite)));
}
async function editSite(id){
  const sites = await getSites();
  const s = sites.find(x=>String(x.id)===String(id));
  if(!s) return;
  openModal({
    title:`Edit Site #${s.id}`, showDelete:true,
    html:`
      <label class="label">Name</label><input class="input" id="m_name" value="${esc(s.name)}"/>
      <label class="label">Base URL</label><input class="input" id="m_base" value="${esc(s.base_url)}"/>
      <label class="label">Publish directory (absolute server path)</label><input class="input" id="m_dir" value="${esc(s.publish_dir)}"/>
      <label class="label">posts.json path (absolute server path)</label><input class="input" id="m_index" value="${esc(s.posts_index_path)}"/>
    `,
    onSave: async ()=>{
      const payload={id:s.id,name:el("#m_name").value.trim(),base_url:el("#m_base").value.trim(),publish_dir:el("#m_dir").value.trim(),posts_index_path:el("#m_index").value.trim()};
      const r=await post(API.crud+"?action=save_site",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    },
    onDelete: async ()=>{
      if(!confirm("Delete this site?")) return;
      const r=await post(API.crud+"?action=delete_site",{id:s.id});
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
}
el("#btnNewSite").addEventListener("click",()=>{
  openModal({
    title:"Add Site",
    html:`
      <label class="label">Name</label><input class="input" id="m_name" placeholder="Studio1Live Blog"/>
      <label class="label">Base URL</label><input class="input" id="m_base" placeholder="https://studio1live.com/blog"/>
      <label class="label">Publish directory (absolute server path)</label><input class="input" id="m_dir" placeholder="/home/USER/public_html/blog"/>
      <label class="label">posts.json path (absolute server path)</label><input class="input" id="m_index" placeholder="/home/USER/public_html/blog/posts.json"/>
    `,
    onSave: async ()=>{
      const payload={name:el("#m_name").value.trim(),base_url:el("#m_base").value.trim(),publish_dir:el("#m_dir").value.trim(),posts_index_path:el("#m_index").value.trim()};
      const r=await post(API.crud+"?action=save_site",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
});

// Tools CRUD
async function loadTools(){
  const u=new URL(API.crud,location.href); u.searchParams.set("action","list_tools");
  const d=await get(u.toString()); const items=d.ok?d.items:[];
  const list=el("#toolList");
  if(!items.length){list.innerHTML=`<div class="card muted">No tools.</div>`;return;}
  list.innerHTML=items.map(t=>`
    <div class="item">
      <div style="flex:1;">
        <h3>${esc(t.name)}</h3>
        <div class="meta"><span class="pill">key: <code>${esc(t.key)}</code></span> <span class="pill">type: ${esc(t.type)}</span></div>
      </div>
      <div><button class="btn ghost" data-edit-tool="${t.id}">Edit</button></div>
    </div>
  `).join("");
  els("[data-edit-tool]").forEach(b=>b.addEventListener("click",()=>editTool(b.dataset.editTool)));
}
async function editTool(id){
  const u=new URL(API.crud,location.href); u.searchParams.set("action","list_tools");
  const d=await get(u.toString()); const items=d.ok?d.items:[];
  const t = items.find(x=>String(x.id)===String(id)); if(!t) return;
  openModal({
    title:`Edit Tool #${t.id}`, showDelete:true,
    html:`
      <label class="label">Key</label><input class="input" id="m_key" value="${esc(t.key)}"/>
      <label class="label">Name</label><input class="input" id="m_name" value="${esc(t.name)}"/>
      <label class="label">Type</label>
      <select class="input" id="m_type">
  ${TOOL_TYPES.map(tp => `<option value="${tp}">${tp}</option>`).join("")}
</select>
      <label class="label">Config JSON</label>
      <textarea class="input textarea" id="m_cfg" rows="6">${esc(t.config_json||"{}")}</textarea>
    `,
    onSave: async ()=>{
      const payload={id:t.id,key:el("#m_key").value.trim(),name:el("#m_name").value.trim(),type:el("#m_type").value.trim(),config_json:el("#m_cfg").value};
      const r=await post(API.crud+"?action=save_tool",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    },
    onDelete: async ()=>{
      if(!confirm("Delete this tool?")) return;
      const r=await post(API.crud+"?action=delete_tool",{id:t.id});
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
}
el("#btnNewTool").addEventListener("click",()=>{
  openModal({
    title:"Add Tool",
    html:`
      <label class="label">Key</label><input class="input" id="m_key" placeholder="my_tool"/>
      <label class="label">Name</label><input class="input" id="m_name" placeholder="My Tool"/>
      <select class="input" id="m_type">
  ${TOOL_TYPES.map(tp => `<option value="${tp}">${tp}</option>`).join("")}
</select>
      <textarea class="input textarea" id="m_cfg" rows="6">{}</textarea>
    `,
    onSave: async ()=>{
      const payload={key:el("#m_key").value.trim(),name:el("#m_name").value.trim(),type:el("#m_type").value.trim(),config_json:el("#m_cfg").value};
      const r=await post(API.crud+"?action=save_tool",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
});

// Agents CRUD
async function loadAgents(){
  const u=new URL(API.crud,location.href); u.searchParams.set("action","list_agents");
  const d=await get(u.toString()); const items=d.ok?d.items:[];
  const list=el("#agentList");
  if(!items.length){list.innerHTML=`<div class="card muted">No agents.</div>`;return;}
  list.innerHTML=items.map(a=>`
    <div class="item">
      <div style="flex:1;">
        <h3>${esc(a.name)}</h3>
        <div class="meta">
          <span class="pill">key: <code>${esc(a.key)}</code></span>
          ${a.preferred_tool_key?`<span class="pill">tool: <code>${esc(a.preferred_tool_key)}</code></span>`:""}
        </div>
        ${a.description?`<div class="meta" style="margin-top:6px;">${esc(a.description)}</div>`:""}
      </div>
      <div><button class="btn ghost" data-edit-agent="${a.id}">Edit</button></div>
    </div>
  `).join("");
  els("[data-edit-agent]").forEach(b=>b.addEventListener("click",()=>editAgent(b.dataset.editAgent)));
}
async function editAgent(id){
  const u=new URL(API.crud,location.href); u.searchParams.set("action","list_agents");
  const d=await get(u.toString()); const items=d.ok?d.items:[];
  const a=items.find(x=>String(x.id)===String(id)); if(!a) return;

  openModal({
    title:`Edit Agent #${a.id}`, showDelete:true,
    html:`
      <label class="label">Key</label><input class="input" id="m_key" value="${esc(a.key)}"/>
      <label class="label">Name</label><input class="input" id="m_name" value="${esc(a.name)}"/>
      <label class="label">Description</label><input class="input" id="m_desc" value="${esc(a.description||"")}"/>
      <label class="label">Preferred Tool Key</label><input class="input" id="m_pt" value="${esc(a.preferred_tool_key||"")}"/>
      <label class="label">Config JSON</label><textarea class="input textarea" id="m_cfg" rows="6">${esc(a.config_json||"{}")}</textarea>
    `,
    onSave: async ()=>{
      const payload={id:a.id,key:el("#m_key").value.trim(),name:el("#m_name").value.trim(),description:el("#m_desc").value.trim(),preferred_tool_key:el("#m_pt").value.trim(),config_json:el("#m_cfg").value};
      const r=await post(API.crud+"?action=save_agent",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    },
    onDelete: async ()=>{
      if(!confirm("Delete this agent?")) return;
      const r=await post(API.crud+"?action=delete_agent",{id:a.id});
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
}
el("#btnNewAgent").addEventListener("click",()=>{
  openModal({
    title:"Add Agent",
    html:`
      <label class="label">Key</label><input class="input" id="m_key" placeholder="legal_writer"/>
      <label class="label">Name</label><input class="input" id="m_name" placeholder="Legal Writer Agent"/>
      <label class="label">Description</label><input class="input" id="m_desc" placeholder="Writes legal-style posts."/>
      <label class="label">Preferred Tool Key</label><input class="input" id="m_pt" placeholder="openai_draft"/>
      <label class="label">Config JSON</label><textarea class="input textarea" id="m_cfg" rows="6">{}</textarea>
    `,
    onSave: async ()=>{
      const payload={key:el("#m_key").value.trim(),name:el("#m_name").value.trim(),description:el("#m_desc").value.trim(),preferred_tool_key:el("#m_pt").value.trim(),config_json:el("#m_cfg").value};
      const r=await post(API.crud+"?action=save_agent",payload);
      if(!r.ok){alert(r.error);return;}
      closeModal(); await refreshAll();
    }
  });
});

// Router demo
el("#btnRoute").addEventListener("click", async ()=>{
  const text = el("#routeText").value.trim();
  if(!text){alert("Type something to route.");return;}
  el("#routeOutput").textContent="Routingâ€¦";
  const res = await post(API.router, {text});
  if(!res.ok){el("#routeOutput").textContent="Error: "+res.error;return;}
  el("#routeOutput").textContent = "Route: " + res.result.route;
});

// Generator
async function initGenerator(){
  const st=await get(API.status);
  el("#genStatus").textContent = (st.ok && st.openai_enabled) ? "OpenAI enabled" : "OpenAI disabled";
}
el("#btnGenerate").addEventListener("click",async()=>{
  const prompt=el("#genPrompt").value.trim();
  if(!prompt){alert("Enter a prompt."); return;}
  el("#genOutput").textContent="Generatingâ€¦";
  const res=await post(API.draft,{prompt,tone:el("#genTone").value.trim(),word_count:Number(el("#genWords").value||1200)});
  if(!res.ok){el("#genOutput").textContent="Error: "+res.error; return;}
  el("#genOutput").innerHTML=`âœ… Created draft <b>#${res.id}</b> â€” <span class="pill">${esc(res.title)}</span>`;
  await refreshAll(); showTab("drafts");
});

// PWA install
let deferred=null;
window.addEventListener("beforeinstallprompt",(e)=>{
  e.preventDefault(); deferred=e;
  const b=el("#btnInstall"); b.style.display="";
  b.addEventListener("click",async()=>{
    b.style.display="none"; deferred.prompt(); await deferred.userChoice; deferred=null;
  },{once:true});
});

function debounce(fn,ms){let t=null; return (...args)=>{clearTimeout(t); t=setTimeout(()=>fn(...args),ms);} }



// Share Packs
async function loadSharePacks(){
  const u = new URL(API.share, location.href);
  u.searchParams.set("action","list");
  u.searchParams.set("status","due");
  const d = await get(u.toString());
  const list = el("#shareList");
  if(!list) return;
  if(!d.ok){ list.innerHTML = `<div class="card muted">Error: ${esc(d.error||"")}</div>`; return; }
  const items = d.items || [];
  if(!items.length){ list.innerHTML = `<div class="card muted">No share packs due.</div>`; return; }

  list.innerHTML = items.map(sp=>{
    let platforms = [];
    try{ platforms = JSON.parse(sp.platforms_json||"[]"); }catch(e){ platforms=[]; }
    return `
      <div class="item">
        <div style="flex:1;">
          <h3>${esc(sp.article_title || "Share Pack")}</h3>
          <div class="meta">
            ${sp.site_name?`<span class="pill">site: ${esc(sp.site_name)}</span>`:""}
            <span class="pill">platforms: ${esc(platforms.join(", "))}</span>
          </div>
          <div class="meta" style="margin-top:6px;"><a href="${esc(sp.url)}" target="_blank" rel="noopener">${esc(sp.url)}</a></div>
          <pre class="codeblock" id="sp_full_${sp.id}" style="margin-top:10px;white-space:pre-wrap;">${esc(sp.full_post)}</pre>
          <div class="row" style="margin-top:10px;">
            <button class="btn ghost" data-copy="#sp_full_${sp.id}">Copy Full Post</button>
            <button class="btn ghost" data-open-seq="${sp.id}">Post Sequence</button>
            <div class="spacer"></div>
            <button class="btn" data-done-sp="${sp.id}">Mark Done</button>
          </div>
        </div>
      </div>
    `;
  }).join("");

  els("[data-copy]").forEach(b=>b.addEventListener("click",()=>copyFrom(b.getAttribute("data-copy"))));
  els("[data-done-sp]").forEach(b=>b.addEventListener("click",()=>markShareDone(b.dataset.doneSp)));
  els("[data-open-seq]").forEach(b=>b.addEventListener("click",()=>openPostSequence(b.dataset.openSeq)));
}

async function markShareDone(id){
  const r = await post(API.share+"?action=mark_done", {id:Number(id)});
  if(!r.ok){ alert(r.error); return; }
  await loadSharePacks();
}

function openPostSequence(id){
  const item = document.querySelector(`#sp_full_${id}`);
  const text = item?.innerText || "";
  navigator.clipboard.writeText(text);

  const platforms = [
    {name:"Facebook", url:"https://www.facebook.com/"},
    {name:"Instagram", url:"https://www.instagram.com/"},
    {name:"TikTok", url:"https://www.tiktok.com/upload"},
    {name:"Pinterest", url:"https://www.pinterest.com/pin-builder/"}
  ];

  let i = 0;
  openModal({title:"Post Sequence", html:`
    <div class="card">
      <p class="muted">We copied the full post to your clipboard. Open each platform, paste, and post. This avoids opening lots of tabs at once.</p>
      <div class="row" style="margin-top:10px;">
        <button class="btn" id="seqNext">Open next platform</button>
        <button class="btn ghost" id="seqCopy">Copy again</button>
        <div class="spacer"></div>
        <span class="pill" id="seqLabel"></span>
      </div>
    </div>
  `});

  const label = el("#seqLabel");
  const update = ()=> label.textContent = (i < platforms.length) ? `Next: ${platforms[i].name}` : "Done";
  update();

  el("#seqCopy").addEventListener("click",()=>navigator.clipboard.writeText(text));
  el("#seqNext").addEventListener("click",()=>{
    if(i >= platforms.length){ closeModal(); return; }
    window.open(platforms[i].url, "_blank", "noopener");
    i++; update();
  });
}

// Settings
async function loadSettings(){
  const u = new URL(API.share, location.href);
  u.searchParams.set("action","settings_get");
  const d = await get(u.toString());
  if(!d.ok) return;
  el("#setAutoShare") && (el("#setAutoShare").value = String(d.settings.auto_sharepack_after_publish ?? "1"));
  const plats = d.settings.default_share_platforms || ["facebook","instagram","tiktok","pinterest"];
  el("#setPlatforms") && (el("#setPlatforms").value = plats.join(", "));
  const curl = new URL("../api/share_due.php", window.location.href);
  el("#cronShareUrl") && (el("#cronShareUrl").textContent = curl.toString());
}

el("#btnRefreshShare")?.addEventListener("click", loadSharePacks);
el("#btnSaveSettings")?.addEventListener("click", async ()=>{
  const plats = (el("#setPlatforms")?.value || "").split(",").map(s=>s.trim()).filter(Boolean);
  const payload = {
      // name will be sanitized server-side too
auto_sharepack_after_publish: el("#setAutoShare")?.value || "1", default_share_platforms: plats};
  const r = await post(API.share+"?action=settings_save", payload);
  if(!r.ok){ alert(r.error); return; }
  alert("Saved settings.");
});



// Tool Registry
async function loadToolsRegistry(){
  const u = new URL(API.tools, location.href);
  u.searchParams.set("action","list");
  const d = await get(u.toString());
  const box = el("#toolsList");
  if(!box) return;
  if(!d.ok){ box.innerHTML = `<div class="card muted">Error: ${esc(d.error||"")}</div>`; return; }
  const items = d.items || [];
  if(!items.length){ box.innerHTML = `<div class="card muted">No tools yet. Click Add Tool.</div>`; return; }

  box.innerHTML = items.map(t=>{
    let cfg = {};
    try{ cfg = JSON.parse(t.config_json||"{}"); }catch(e){ cfg={}; }
    return `
      <div class="item">
        <div style="flex:1;">
          <h3>${esc(t.name)} ${t.is_enabled==1?``:`<span class="pill">disabled</span>`}</h3>
          <div class="meta">
            <span class="pill">kind: ${esc(t.kind)}</span>
            ${cfg.model?`<span class="pill">model: ${esc(cfg.model)}</span>`:""}
          </div>
          <pre class="codeblock" style="white-space:pre-wrap;margin-top:10px;">${esc(JSON.stringify(cfg, null, 2))}</pre>
          <div class="row" style="margin-top:10px;">
            <button class="btn ghost" data-tool-edit="${t.id}">Edit</button>
            <div class="spacer"></div>
            <button class="btn danger" data-tool-del="${t.id}">Delete</button>
          </div>
        </div>
      </div>
    `;
  }).join("");

  els("[data-tool-edit]").forEach(b=>b.addEventListener("click",()=>openToolEditor(Number(b.dataset.toolEdit))));
  els("[data-tool-del]").forEach(b=>b.addEventListener("click",()=>deleteTool(Number(b.dataset.toolDel))));
}

function openToolEditor(id, preset=null){
  const defaults = preset || {id:id||0, name:`tool_${Date.now()}`, kind:"openai_text", is_enabled:1, config:{base_url:"https://api.openai.com/v1/responses", edits_url:"https://api.openai.com/v1/images/edits", model:"gpt-4.1-mini", api_key:""}};
  const cfg = defaults.config || {};
  openModal({title: id? "Edit Tool":"Add Tool", html:`
    <div class="card">
      <label class="label">Name</label>
      <input id="toolName" class="input" value="${esc(defaults.name||"")}" placeholder="openai_text / openai_image / openai_video"/>
      <label class="label" style="margin-top:10px;">Kind</label>
      <select id="toolKind" class="input">
        <option value="openai_text">openai_text</option>
        <option value="openai_image">openai_image</option>
        <option value="openai_video">openai_video</option>
        <option value="webhook">webhook</option>
      </select>
      <label class="label" style="margin-top:10px;">Enabled</label>
      <select id="toolEnabled" class="input">
        <option value="1">YES</option>
        <option value="0">NO</option>
      </select>

      <div class="grid2" style="margin-top:12px;">
        <div>
          <label class="label">Edits URL (images)</label>
          <input id="toolEdits" class="input" value="${esc(cfg.edits_url||"https://api.openai.com/v1/images/edits")}" />
        </div>
        <div>
          <label class="label">Base URL</label>
          <input id="toolBase" class="input" value="${esc(cfg.base_url||"")}" />
        </div>
        <div>
          <label class="label">Model</label>
          <input id="toolModel" class="input" value="${esc(cfg.model||"")}" />
        </div>
      </div>

      <label class="label" style="margin-top:12px;">API Key (stored in DB)</label>
      <input id="toolKey" class="input" value="${esc(cfg.api_key||"")}" placeholder="sk-..." />

      <div class="row" style="margin-top:14px;">
        <button class="btn" id="toolSave">Save</button>
        <div class="spacer"></div>
        <button class="btn ghost" onclick="closeModal()">Close</button>
      </div>
      <p class="muted" style="margin-top:10px;">Tip: For OpenAI image generation, set kind=openai_image and model=gpt-image-1 (or your preferred). For text, use responses endpoint.</p>
    </div>
  `});

  el("#toolKind").value = defaults.kind || "openai_text";
  el("#toolEnabled").value = String(defaults.is_enabled ?? 1);

  el("#toolSave").addEventListener("click", async ()=>{
    const payload = {
      // name will be sanitized server-side too

      id: id||0,
      name: el("#toolName").value.trim(),
      kind: el("#toolKind").value,
      type: el("#toolKind").value, // compatibility

      is_enabled: Number(el("#toolEnabled").value),
      config: {
        base_url: el("#toolBase").value.trim(),
        edits_url: el("#toolEdits").value.trim(),
        model: el("#toolModel").value.trim(),
        api_key: el("#toolKey").value.trim(),
        size: "1024x1024"
      }
    };
        if(!payload.name){ payload.name = `tool_${Date.now()}`; }
    const r = await post(API.tools+"?action=save", payload);
    if(!r.ok){ alert(r.error); return; }
    closeModal();
    await loadToolsRegistry();
  });
}

async function deleteTool(id){
  if(!confirm("Delete this tool?")) return;
  const r = await post(API.tools+"?action=delete", {id});
  if(!r.ok){ alert(r.error); return; }
  await loadToolsRegistry();
}

el("#btnToolNew")?.addEventListener("click", ()=>openToolEditor(0));
el("#btnToolRefresh")?.addEventListener("click", loadToolsRegistry);

// Media helpers inside Article Editor
let __currentArticleId = 0;

function setCurrentArticleIdFromForm(){
  const idEl = el("#articleId");
  if(!idEl) return;
  __currentArticleId = Number(idEl.value || 0);
  loadArticleMedia();
}

async function loadArticleMedia(){
  const box = el("#articleMedia");
  if(!box || !__currentArticleId) return;
  const u = new URL(API.media, location.href);
  u.searchParams.set("action","list_by_article");
  u.searchParams.set("article_id", String(__currentArticleId));
  const d = await get(u.toString());
  if(!d.ok){ box.innerHTML = `<div class="card muted">Media error: ${esc(d.error||"")}</div>`; return; }
  const items = d.items || [];
  if(!items.length){ box.innerHTML = `<div class="card muted">No media yet. Generate or upload.</div>`; return; }

  box.innerHTML = items.map(m=>{
    const isImg = m.type === "image";
    const rel = "../"+m.file_path;
    const preview = isImg ? `<img src="${esc(rel)}" style="max-width:240px;border-radius:14px;display:block;" />`
                          : `<video src="${esc(rel)}" controls style="max-width:320px;border-radius:14px;"></video>`;
    return `
      <div class="item">
        <div style="flex:1;">
          <div class="row">
            <h3 style="margin:0;">${esc(m.type)} <span class="pill">${esc(m.source)}</span></h3>
            <div class="spacer"></div>
            ${m.type==="image"?`<button class="btn ghost" data-media-edit="${m.id}">AI Edit</button> <button class="btn ghost" data-media-crop="${m.id}" data-media-url="${rel}">Crop/Resize</button>`:""}
            ${m.type==="video" && (!m.file_path || m.file_path==="")?`<button class="btn ghost" data-media-status="${m.id}">Refresh Status</button>`:""}
            <button class="btn danger" data-media-del="${m.id}">Delete</button>
          </div>
          <div class="meta" style="margin-top:6px;">${esc(m.prompt||"")}</div>
          <div style="margin-top:10px;">${preview}</div>
          <div class="meta" style="margin-top:8px;"><a href="${esc(rel)}" target="_blank" rel="noopener">Open file</a></div>
        </div>
      </div>
    `;
  }).join("");

  els("[data-media-del]").forEach(b=>b.addEventListener("click", ()=>deleteMedia(Number(b.dataset.mediaDel))));
  els("[data-media-edit]").forEach(b=>b.addEventListener("click", ()=>aiEditImage(Number(b.dataset.mediaEdit))));
  els("[data-media-crop]").forEach(b=>b.addEventListener("click", ()=>openCropResize(Number(b.dataset.mediaCrop), b.dataset.mediaUrl)));
  els("[data-media-status]").forEach(b=>b.addEventListener("click", ()=>refreshVideoStatus(Number(b.dataset.mediaStatus))));
}

async function deleteMedia(id){
  if(!confirm("Delete media file?")) return;
  const r = await post(API.media+"?action=delete", {id});
  if(!r.ok){ alert(r.error); return; }
  await loadArticleMedia();
}

async function generateImageForArticle(){
  const prompt = el("#mediaImgPrompt")?.value?.trim() || "";
  if(!prompt){ alert("Enter an image prompt first."); return; }
  if(!__currentArticleId){ alert("Save the article first, then generate media."); return; }
  const r = await post(API.media+"?action=generate_image", {article_id: __currentArticleId, prompt, tool_name:"openai_image"});
  if(!r.ok){ alert(r.error); return; }
  await loadArticleMedia();
}

function openUpload(type){
  if(!__currentArticleId){ alert("Save the article first, then upload media."); return; }
  const input = el("#mediaFileInput");
  input.accept = (type==="image") ? "image/*" : "video/*";
  input.onchange = async ()=>{
    const file = input.files[0];
    if(!file) return;
    const fd = new FormData();
    fd.append("article_id", String(__currentArticleId));
    fd.append("type", type);
    fd.append("prompt", "upload");
    fd.append("file", file);

    const res = await fetch(API.media+"?action=upload", {method:"POST", body: fd});
    const d = await res.json().catch(()=>({ok:false,error:"Non-JSON response"}));
    if(!d.ok){ alert(d.error||"Upload failed"); return; }
    input.value = "";
    await loadArticleMedia();
  };
  input.click();
}

el("#btnMediaRefresh")?.addEventListener("click", loadArticleMedia);
el("#btnGenImage")?.addEventListener("click", generateImageForArticle);
el("#btnUploadImage")?.addEventListener("click", ()=>openUpload("image"));
el("#btnUploadVideo")?.addEventListener("click", ()=>openUpload("video"));
el("#btnGenVideo")?.addEventListener("click", generateVideoForArticle);


async function refreshAll(){
  sitesCache=null;
  await loadDrafts();
  await loadQueue();
  await loadSites();
  await loadTools();
  await loadAgents();
  await initGenerator();
  setCronUrl();
}

function setCronUrl(){
  const url = new URL("../api/publish_due.php", window.location.href);
  el("#cronUrl").textContent = url.toString();
}

// boot
(async function(){ await refreshAll(); })();

// JacAI Console + Voice
let jacLastReply = "";

function jacAdd(role, text){
  const chat = el("#jacChat");
  if(!chat) return;
  const div = document.createElement('div');
  div.className = 'msg ' + (role==='me' ? 'me' : 'ai');
  div.textContent = text;
  chat.appendChild(div);
  chat.scrollTop = chat.scrollHeight;
}

async function jacaiSend(text){
  if(!text) return;
  jacAdd('me', text);
  el('#jacInput') && (el('#jacInput').value = '');
  const r = await post(API.jacai, {message: text});
  if(!r.ok){
    jacAdd('ai', 'Error: ' + (r.error||'unknown'));
    jacLastReply = r.error||'';
    return;
  }
  const reply = r.result?.reply || JSON.stringify(r.result||{}, null, 2);
  jacAdd('ai', reply);
  jacLastReply = reply;
  // refresh lists so user sees new drafts/sharepacks immediately
  try { await loadArticles(); await loadSharePacks(); } catch(e) {}
}

// Quick buttons
els('[data-jac-quick]').forEach(b=>b.addEventListener('click',()=>jacaiSend(b.getAttribute('data-jac-quick'))));
el('#btnJacSend')?.addEventListener('click',()=>jacaiSend(el('#jacInput')?.value?.trim()));
el('#jacInput')?.addEventListener('keydown',(e)=>{ if((e.ctrlKey||e.metaKey) && e.key==='Enter'){ jacaiSend(el('#jacInput')?.value?.trim()); }});
el('#btnJacHelp')?.addEventListener('click',()=>jacaiSend('help'));

// Voice: SpeechRecognition (STT)
let jacRec = null;
let jacListening = false;
function jacSetVoiceStatus(s){ const p=el('#jacVoiceStatus'); if(p) p.textContent = 'Voice: '+s; }

function jacInitSTT(){
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
  if(!SR) return null;
  const rec = new SR();
  rec.lang = 'en-US';
  rec.interimResults = true;
  rec.continuous = false;
  rec.onstart = ()=>{ jacListening=true; jacSetVoiceStatus('listening'); };
  rec.onerror = (e)=>{ jacListening=false; jacSetVoiceStatus('error'); console.warn(e); };
  rec.onend = ()=>{ jacListening=false; jacSetVoiceStatus('idle'); };
  rec.onresult = (ev)=>{
    let txt='';
    for(let i=ev.resultIndex;i<ev.results.length;i++) txt += ev.results[i][0].transcript;
    if(el('#jacInput')) el('#jacInput').value = txt.trim();
  };
  return rec;
}

el('#btnJacMic')?.addEventListener('click',()=>{
  if(!jacRec) jacRec = jacInitSTT();
  if(!jacRec){ alert('Voice input not supported in this browser. Try Chrome.'); return; }
  if(jacListening){ try{jacRec.stop();}catch(e){} return; }
  try{ jacRec.start(); }catch(e){ console.warn(e); }
});

// Voice: SpeechSynthesis (TTS)
el('#btnJacSpeak')?.addEventListener('click',()=>{
  if(!('speechSynthesis' in window)){ alert('Text-to-speech not supported.'); return; }
  const u = new SpeechSynthesisUtterance(jacLastReply || 'No reply yet.');
  u.rate = 1.0;
  window.speechSynthesis.cancel();
  window.speechSynthesis.speak(u);
});

// -------------------- Phase 9: Accounts + Providers + Cron UI --------------------
async function refreshAccounts(){
  const r = await get(API.accounts + "?action=list");
  const elList = el("#accountsList"); if(!elList) return;
  elList.innerHTML = "";
  (r.rows||[]).forEach(a=>{
    const div = document.createElement("div");
    div.className = "listitem";
    div.innerHTML = `
      <div><strong>${esc(a.platform)}</strong> <span class="muted">${esc(a.account_name||"")}</span></div>
      <div class="muted" style="font-size:12px">auth: ${esc(a.auth_type)} ${a.expires_at?(" â€¢ exp: "+esc(a.expires_at)):""}</div>
      <div class="row" style="margin-top:8px">
        <button class="btn danger" data-act="del" data-id="${a.id}">Delete</button>
      </div>
    `;
    elList.appendChild(div);
  });
}

async function refreshProviders(){
  const r = await get(API.providers + "?action=list");
  const elList = el("#providersList"); if(!elList) return;
  elList.innerHTML = "";
  (r.rows||[]).forEach(p=>{
    const div = document.createElement("div");
    div.className = "listitem";
    div.innerHTML = `
      <div><strong>${esc(p.name)}</strong> <span class="muted">(${esc(p.platform)})</span></div>
      <div class="muted" style="font-size:12px">${esc(p.auth_url)}</div>
      <div class="row" style="margin-top:8px">
        <button class="btn danger" data-act="delp" data-id="${p.id}">Delete</button>
      </div>
    `;
    elList.appendChild(div);
  });
}

function openAccountModal(){
  openModal({title:"Add Account (manual token)", html:`
    <div class="form">
      <label>Platform</label>
      <select id="accPlatform">
        <option value="linkedin">linkedin</option>
        <option value="pinterest">pinterest</option>
        <option value="x">x</option>
        <option value="facebook">facebook</option>
        <option value="instagram">instagram</option>
        <option value="tiktok">tiktok</option>
      </select>
      <label>Account name (label)</label>
      <input id="accName" placeholder="e.g., Studio1Live LinkedIn"/>
      <label>Access token</label>
      <textarea id="accToken" rows="4" placeholder="paste token here"></textarea>
      <label>Refresh token (optional)</label>
      <textarea id="accRefresh" rows="2"></textarea>
      <label>Expires at (ISO optional)</label>
      <input id="accExp" placeholder="2026-01-01T00:00:00Z"/>
      <label>Enabled</label>
      <label class="check"><input type="checkbox" id="accEnabled" checked/> enabled</label>
      <div class="row">
        <button class="btn" id="accSave">Save</button>
        <button class="btn ghost" data-close>Cancel</button>
      </div>
    </div>
  `});
  setTimeout(()=>{
    el("#accSave").onclick = async ()=>{
      const payload = {
        platform: el("#accPlatform").value.trim(),
        account_name: el("#accName").value.trim(),
        auth_type: "manual",
        access_token: el("#accToken").value.trim(),
        refresh_token: el("#accRefresh").value.trim(),
        expires_at: el("#accExp").value.trim(),
        enabled: el("#accEnabled").checked ? 1 : 0,
        token_json: {}
      };
      const r = await post(API.accounts + "?action=save", payload);
      closeModal();
      await refreshAccounts();
      toast("Saved account #" + r.id);
    };
  },0);
}

function openProviderModal(){
  openModal({title:"Add OAuth Provider (store settings)", html:`
    <div class="form">
      <label>Name</label><input id="provName" placeholder="LinkedIn OAuth"/>
      <label>Platform</label><input id="provPlatform" placeholder="linkedin"/>
      <label>Client ID</label><input id="provCID" placeholder="..."/>
      <label>Client Secret</label><input id="provCS" placeholder="..."/>
      <label>Auth URL</label><input id="provAuth" placeholder="https://.../authorize"/>
      <label>Token URL</label><input id="provToken" placeholder="https://.../token"/>
      <label>Scopes</label><input id="provScopes" placeholder="space-separated scopes"/>
      <label>Enabled</label>
      <label class="check"><input type="checkbox" id="provEnabled" checked/> enabled</label>
      <div class="row">
        <button class="btn" id="provSave">Save</button>
        <button class="btn ghost" data-close>Cancel</button>
      </div>
    </div>
  `});
  setTimeout(()=>{
    el("#provSave").onclick = async ()=>{
      const payload = {
        name: el("#provName").value.trim(),
        platform: el("#provPlatform").value.trim(),
        client_id: el("#provCID").value.trim(),
        client_secret: el("#provCS").value.trim(),
        auth_url: el("#provAuth").value.trim(),
        token_url: el("#provToken").value.trim(),
        scopes: el("#provScopes").value.trim(),
        enabled: el("#provEnabled").checked ? 1 : 0,
        extra_json: {}
      };
      const r = await post(API.providers + "?action=save", payload);
      closeModal();
      await refreshProviders();
      toast("Saved provider #" + r.id);
    };
  },0);
}
async function sendToZapier(articleId){
  articleId = Number(articleId || 0);
  if(!articleId){
    toast("Missing article id");
    return;
  }

  // No DOM lookups here. Works from drafts list, editor modal, anywhere.
  let r;
  try{
    r = await post(API.zapier + "?action=send", { id: articleId });
  }catch(e){
    console.error(e);
    toast("Zapier send failed (network/server). See console.");
    return;
  }

  if(!r || !r.ok){
    console.error("Zapier send failed", r);
    toast("Zapier send failed: " + (r?.error || "unknown"));
    return;
  }

  toast("Sent to Zapier ✅" + (r.request_id ? (" " + r.request_id) : ""));
}

async function refreshCron(){
  const r = await get(API.cron + "?action=list");
  const elList = el("#cronList"); if(!elList) return;
  elList.innerHTML = "";
  (r.rows||[]).forEach(j=>{
    const div = document.createElement("div");
    div.className = "listitem";
    div.innerHTML = `
      <div><strong>${esc(j.job_type)}</strong> <span class="muted">run: ${esc(j.run_at)}</span></div>
      <div class="muted" style="font-size:12px">status: ${esc(j.status)} â€¢ attempts: ${esc(j.attempts)} ${j.last_error?(" â€¢ err: "+esc(j.last_error)):""}</div>
      <div class="row" style="margin-top:8px">
        <button class="btn danger" data-act="delj" data-id="${j.id}">Delete</button>
      </div>
    `;
    elList.appendChild(div);
  });
}

function openCronModal(){
  openModal({title:"Enqueue Cron Job", html:`
    <div class="form">
      <label>Job type</label>
      <select id="cronType">
        <option value="publish_site">publish_site</option>
        <option value="oauth_post">oauth_post</option>
      </select>
      <label>Run at (ISO)</label>
      <input id="cronRunAt" value="${new Date(Date.now()+5*60000).toISOString()}"/>
      <label>Payload JSON</label>
      <textarea id="cronPayload" rows="6">{
  "tool_key": "example_oauth_post",
  "account_id": 1,
  "text": "Hello world",
  "url": "https://example.com"
}</textarea>
      <div class="row">
        <button class="btn" id="cronSave">Enqueue</button>
        <button class="btn ghost" data-close>Cancel</button>
      </div>
    </div>
  `});
  setTimeout(()=>{
    el("#cronSave").onclick = async ()=>{
      let payload = {};
      try{ payload = JSON.parse(el("#cronPayload").value.trim()||"{}"); }catch(e){ payload = {}; }
      const r = await post(API.cron + "?action=enqueue", {job_type: el("#cronType").value, run_at: el("#cronRunAt").value.trim(), payload});
      closeModal();
      await refreshCron();
      toast("Enqueued job #" + r.id);
    };
  },0);
}

document.addEventListener("click", async (e)=>{
  const t = e.target;
  if (t && t.id === "btnAccountsRefresh") return refreshAccounts();
  if (t && t.id === "btnProvidersRefresh") return refreshProviders();
  if (t && t.id === "btnAccountAdd") return openAccountModal();
  if (t && t.id === "btnProviderAdd") return openProviderModal();
  if (t && t.id === "btnCronRefresh") return refreshCron();
  if (t && t.id === "btnCronAdd") return openCronModal();

  if (t && t.dataset && t.dataset.act === "del") {
    const id = parseInt(t.dataset.id,10);
    await post(API.accounts + "?action=delete", {id});
    await refreshAccounts();
    toast("Deleted account #" + id);
  }
  if (t && t.dataset && t.dataset.act === "delp") {
    const id = parseInt(t.dataset.id,10);
    await post(API.providers + "?action=delete", {id});
    await refreshProviders();
    toast("Deleted provider #" + id);
  }
  if (t && t.dataset && t.dataset.act === "delj") {
    const id = parseInt(t.dataset.id,10);
    await post(API.cron + "?action=delete", {id});
    await refreshCron();
    toast("Deleted job #" + id);
  }
});

document.addEventListener("DOMContentLoaded", ()=>{
  document.querySelectorAll(".navbtn").forEach(btn=>{
    btn.addEventListener("click", ()=>{
      const tab = btn.getAttribute("data-tab");
      if (tab==="accounts"){ refreshAccounts(); refreshProviders(); }
      if (tab==="cron"){ refreshCron(); }
    });
  });
});
// -------------------------------------------------------------------------------
