// dashboard-x-run-from-here-background-tab.js — extracted verbatim from dashboard.js (pure move).

// ── X "Run from here" (background-tab adapter) ──────────────────────
// When an X action carries `runFromHere` (platform-actions.js), the detail
// modal offers "Configure & run →" instead of "Open X.com →". This collects
// the action's fields, fires its swAction (background.js spawns a hidden x.com
// worker tab and runs the action there), and streams progress into a floating
// drawer via the SAME nx-batch-progress relay LinkedIn uses. Self-contained:
// builds its own drawer + progress listener so it works on any tab.

let _xRunBatchListener = null;

function _openXRunConfigModal(action, prefill) {
  const rf = action.runFromHere;
  const fields = (rf && rf.fields) || [];
  const inputs = {};
  const pf = prefill || {}; // preserve values when returning from Preview
  const form = el("form", {
    style: "display:flex;flex-direction:column;gap:12px;",
    onsubmit: (e) => e.preventDefault(),
  });
  form.appendChild(
    el(
      "p",
      { class: "muted", style: "margin:0 0 4px;font-size:12px;line-height:1.5;" },
      action.description,
    ),
  );
  form.appendChild(
    el(
      "div",
      {
        style:
          "font-size:11px;color:#93c5fd;background:#0f1620;border:1px solid #1f3048;border-radius:6px;padding:8px 10px;line-height:1.5;",
      },
      "Runs in a hidden x.com window — you don't need X open. Progress appears bottom-right.",
    ),
  );

  const inputBase =
    "background:#0f0f15;border:1px solid #2a2a38;color:#e0e0e8;border-radius:6px;padding:6px 8px;font-size:12px;font-family:inherit;";
  for (const f of fields) {
    const row = el("div", {});
    let input;
    const pfVal = pf[f.id];
    if (f.type === "boolean") {
      // Checkbox: label wraps the input, no separate label line.
      input = el("input", { type: "checkbox" });
      input.checked = pfVal != null ? !!pfVal : !!f.default;
      const lbl = el(
        "label",
        {
          style:
            "display:flex;align-items:center;gap:8px;font-size:12px;color:#e0e0e8;cursor:pointer;",
        },
        [input, document.createTextNode(f.label)],
      );
      row.appendChild(lbl);
    } else {
      row.appendChild(
        el(
          "label",
          { style: "display:block;font-size:11px;color:#888;margin-bottom:4px;" },
          f.label,
        ),
      );
      if (f.type === "number") {
        input = el("input", {
          type: "number",
          value: String(pfVal ?? f.default ?? ""),
          min: f.min != null ? String(f.min) : null,
          max: f.max != null ? String(f.max) : null,
          style: "width:120px;" + inputBase,
        });
        row.appendChild(input);
      } else if (f.type === "select") {
        const sel = pfVal ?? f.default;
        input = el(
          "select",
          { style: "width:100%;" + inputBase },
          (f.options || []).map(([val, lab]) => {
            const o = el("option", { value: val }, lab || val);
            if (val === sel) o.selected = true;
            return o;
          }),
        );
        row.appendChild(input);
      } else if (f.type === "textarea") {
        input = el(
          "textarea",
          {
            rows: "2",
            placeholder: f.placeholder || "",
            style: "width:100%;resize:vertical;" + inputBase,
          },
          String(pfVal ?? f.default ?? ""),
        );
        row.appendChild(input);
      } else {
        // text
        input = el("input", {
          type: "text",
          value: String(pfVal ?? f.default ?? ""),
          placeholder: f.placeholder || "",
          style: "width:100%;" + inputBase,
        });
        row.appendChild(input);
      }
    }
    form.appendChild(row);
    inputs[f.id] = { input, field: f };
  }

  const _h = {};
  // Collect + validate the form into a config object (null on validation fail).
  const collectConfig = () => {
    const config = {};
    for (const [id, { input, field }] of Object.entries(inputs)) {
      config[id] = field.type === "boolean" ? input.checked : input.value;
    }
    // Required: a non-boolean field with no default + no `optional:true` must be filled.
    for (const f of fields) {
      if (f.type === "boolean" || f.type === "select") continue;
      if (f.optional || f.default) continue;
      if (!String(config[f.id] || "").trim()) {
        toast(`⚠ ${f.label} required`, "err");
        return null;
      }
    }
    return config;
  };
  const cancelBtn = el(
    "button",
    {
      class: "btn btn-secondary",
      onclick: () => {
        if (_h.close) _h.close();
      },
    },
    "Cancel",
  );
  // Preview: map out exactly what this will do, with your inputs — NOTHING runs.
  const previewBtn = el(
    "button",
    {
      class: "btn btn-secondary",
      title: "See exactly what this will do — nothing runs",
      onclick: () => {
        const config = collectConfig();
        if (!config) return;
        if (_h.close) _h.close();
        _openXPreviewModal(action, config, fields);
      },
    },
    "🔎 Preview",
  );
  const runBtn = el(
    "button",
    {
      class: "btn btn-primary",
      onclick: () => {
        const config = collectConfig();
        if (!config) return;
        if (_h.close) _h.close();
        _xRunStartBatch(action, config);
      },
    },
    "Run now →",
  );

  const h = openModal({
    title: action.label,
    body: form,
    actions: [cancelBtn, previewBtn, runBtn],
    maxWidth: "460px",
  });
  _h.close = h.close;
}

// Plain-English gloss for a workflow node type, so the step map reads as an
// explanation ("collects the follower list from X") rather than a code symbol
// ("browser.scrape_followers"). Exact-type match first, then a category fallback.
const _NODE_EXPLAIN = {
  "trigger.manual": "starts when you click Run",
  "trigger.cron": "runs automatically on a schedule",
  "browser.navigate": "opens the page",
  "browser.parse_post_url": "reads the target profile/post link",
  "browser.scrape_followers": "collects the follower list",
  "browser.scrape_commenters": "collects everyone who commented",
  "browser.scrape_invites": "reads your pending invitations",
  "browser.scan_feed": "scans the feed / list of results",
  "browser.search": "searches for matching posts/people",
  "browser.view_profile": "opens the profile to read it",
  "browser.follow_user": "follows them",
  "browser.unfollow_user": "unfollows them",
  "browser.like_post": "likes the post",
  "browser.retweet": "reposts it",
  "browser.reply": "posts an AI-drafted reply",
  "browser.compose_post": "publishes a post",
  "browser.send_message": "sends a direct message",
  "browser.send_connect": "sends a connection request",
  "browser.accept_invite": "accepts the invitation",
  "browser.withdraw_invite": "withdraws the sent invitation",
  "logic.loop": "repeats the next steps for each one",
  "logic.if": "only continues if the check passes",
  "logic.filter": "keeps only the ones that match",
  "logic.switch": "branches depending on the value",
  "logic.wait": "waits a bit (human-like pacing)",
  "logic.cooldown": "pauses to respect cooldowns",
  "logic.rate_limit": "stays within safe daily limits",
  "logic.merge": "combines the results",
  "ai.match_icp": "an AI judges if they fit your ideal customer",
  "ai.score_icp_lookalike": "an AI scores how good a match they are",
  "ai.classify_signal": "an AI judges relevance/intent",
  "ai.generate_reply": "an AI writes a tailored reply",
  "ai.generate_comment": "an AI writes a tailored comment",
  "ai.generate_message": "an AI writes a tailored message",
  "ai.generate_promo": "an AI writes the post",
  "db.upsert_contact": "saves the person to your CRM",
  "db.upsert_company": "saves the company to your CRM",
  "db.query": "looks up data you already have",
  "db.record_event": "logs the action so you can review it later",
  "notify.telegram_user": "notifies you with the results",
  "outreach.send_dm": "sends the outreach DM",
  "enrich.queue_task": "queues a lookup for contact details",
  "enrich.discover_employees": "finds the right person at the company",
};
function _stepGloss(type) {
  if (!type) return "";
  if (_NODE_EXPLAIN[type]) return _NODE_EXPLAIN[type];
  const cat = String(type).split(".")[0];
  return (
    {
      browser: "acts in the background browser tab",
      ai: "an AI evaluates this step",
      logic: "a control step (timing / branching)",
      db: "saves or reads your data",
      enrich: "finds extra details",
      outreach: "sends a message",
      notify: "notifies you",
      trigger: "kicks off the run",
    }[cat] || ""
  );
}

// Preview modal — shows the resolved settings + the exact step map for an X
// action, WITHOUT running it. "Run it now" proceeds; "Edit inputs" returns to
// the config modal with the entered values preserved.
function _openXPreviewModal(action, config, fields) {
  const nodes = (action.moduleMap && action.moduleMap.nodes) || [];
  const fieldById = {};
  for (const f of fields || []) fieldById[f.id] = f;

  const body = el("div", { style: "display:flex;flex-direction:column;gap:14px;" });
  body.appendChild(
    el(
      "div",
      {
        style:
          "font-size:11px;color:#fbbf24;background:#1a1606;border:1px solid #3a3206;border-radius:6px;padding:8px 10px;line-height:1.5;",
      },
      "Preview only — nothing has run yet. When you click “Run it now”, here's exactly what happens, step by step:",
    ),
  );

  // Your settings (resolved from the form)
  const settings = Object.entries(config).filter(([, v]) => v !== "" && v != null);
  if (settings.length) {
    body.appendChild(
      el(
        "h3",
        {
          style:
            "margin:0;font-size:11px;color:#888;text-transform:uppercase;letter-spacing:0.6px;",
        },
        "Your settings",
      ),
    );
    const dl = el("div", { style: "display:flex;flex-direction:column;gap:4px;font-size:12px;" });
    for (const [id, v] of settings) {
      const label = (fieldById[id] && fieldById[id].label) || id;
      dl.appendChild(
        el("div", {}, [
          el("span", { style: "color:#888;" }, label + ": "),
          el(
            "span",
            { style: "color:#e0e0e8;" },
            String(v === true ? "yes" : v === false ? "no" : v),
          ),
        ]),
      );
    }
    body.appendChild(dl);
  }

  // The exact steps
  body.appendChild(
    el(
      "h3",
      {
        style: "margin:0;font-size:11px;color:#888;text-transform:uppercase;letter-spacing:0.6px;",
      },
      `Steps (${nodes.length})`,
    ),
  );
  if (nodes.length) {
    const ol = el("ol", {
      style: "margin:0;padding-left:24px;font-size:13px;line-height:1.7;color:#ddd;",
    });
    for (const n of nodes) {
      const gloss = _stepGloss(n.type);
      ol.appendChild(
        el("li", { style: "margin-bottom:6px;" }, [
          el("span", { style: "color:#e8e8ef;" }, n.name || n.id),
          gloss ? el("span", { style: "color:#8a8a98;" }, ` — ${gloss}`) : null,
        ]),
      );
    }
    body.appendChild(ol);
  } else {
    body.appendChild(
      el(
        "p",
        { class: "muted", style: "font-size:12px;" },
        "No step map available for this action yet.",
      ),
    );
  }

  const _h = {};
  const editBtn = el(
    "button",
    {
      class: "btn btn-secondary",
      onclick: () => {
        if (_h.close) _h.close();
        _openXRunConfigModal(action, config);
      },
    },
    "← Edit inputs",
  );
  const runBtn = el(
    "button",
    {
      class: "btn btn-primary",
      onclick: () => {
        if (_h.close) _h.close();
        _xRunStartBatch(action, config);
      },
    },
    "Run it now →",
  );
  const h = openModal({
    title: `Preview · ${action.label}`,
    body,
    actions: [editBtn, runBtn],
    maxWidth: "520px",
  });
  _h.close = h.close;
}

async function _xRunStartBatch(action, config) {
  const rf = action.runFromHere || {};
  // Correlation id for this whole invocation. Threaded through background →
  // content script → bot API → IPC → worker so every layer's trace rows
  // stitch together. Shown in the drawer so a stuck run can be looked up via
  // `npm run x-trace <runId>`.
  const runId = `x_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
  _xRunCurrentRunId = runId; // so the drawer's 📸 Replay button knows which run
  _openXRunDrawer(action.label);
  _setupXRunBatchListener();
  _xRunDrawerAppendLog("…", `Starting ${action.label}… (run ${runId})`);
  let result;
  try {
    result = await new Promise((resolve) => {
      chrome.runtime.sendMessage(
        {
          action: "tw-run-action-bg",
          actionId: rf.actionId || action.id,
          url: rf.url,
          config,
          runId,
        },
        (r) => {
          if (chrome.runtime.lastError) resolve({ error: chrome.runtime.lastError.message });
          else resolve(r || { error: "no_response" });
        },
      );
    });
  } catch (err) {
    result = { error: err?.message || String(err) };
  }
  if (result?.error) {
    _xRunDrawerAppendLog("X", `Failed: ${result.error}`);
    toast(`X action failed: ${result.error}`, "err", 6000);
  } else {
    _xRunDrawerAppendLog("✓", `Done${result.account ? ` (@${result.account})` : ""}`);
    toast("✓ X action completed", "ok", 4000);
  }
}

// Full action history for the current run — every step, with its post/message
// link if one was reported. Drives the rendered log + the "Copy" button so you
// can paste the whole run (with links) back for debugging.
let _xRunDrawerHistory = [];
let _xRunCurrentRunId = null;
let _liRunCurrentRunId = null;

// Visual replay — fetch the per-step screenshots captured during a run and show
// them in step order. Best-effort: empty state if capture didn't run / was off.
async function _openRunReplayModal(runId) {
  if (!runId) {
    toast("No run to replay yet", "err");
    return;
  }
  const body = el("div", {
    style: "display:flex;flex-direction:column;gap:14px;max-height:70vh;overflow-y:auto;",
  });
  body.appendChild(el("div", { class: "muted", style: "font-size:11px;" }, `Run ${runId}`));
  const list = el("div", { style: "display:flex;flex-direction:column;gap:16px;" }, [
    el("div", { class: "muted", style: "font-size:12px;" }, "Loading screenshots…"),
  ]);
  body.appendChild(list);
  openModal({ title: "📸 Step-by-step replay", body, maxWidth: "640px" });

  let data;
  try {
    data = await api("twitter-run-shots", { queryString: `?runId=${encodeURIComponent(runId)}` });
  } catch (err) {
    data = { error: err.message };
  }
  list.innerHTML = "";
  const shots = (data && data.shots) || [];
  if (!shots.length) {
    list.appendChild(
      el(
        "div",
        { class: "muted", style: "font-size:12px;line-height:1.6;" },
        data && data.error
          ? `Couldn't load the replay (${data.error}).`
          : "No screenshots for this run yet. Captures appear here a moment after each step runs — reopen this once the run has progressed. (If the worker tab couldn't be attached, capture is skipped and the run still completes.)",
      ),
    );
    return;
  }
  for (const s of shots) {
    const cap = s.stepName || `Step ${s.stepIndex + 1}`;
    list.appendChild(
      el("div", { style: "border:1px solid #2a2a38;border-radius:8px;overflow:hidden;" }, [
        el(
          "div",
          {
            style:
              "padding:8px 10px;font-size:12px;color:#e8e8ef;border-bottom:1px solid #2a2a38;display:flex;gap:8px;align-items:center;",
          },
          [
            el("span", { style: "color:#888;font-family:monospace;" }, `${s.stepIndex + 1}.`),
            el("span", { style: "flex:1;" }, cap),
            s.status === "fail"
              ? el("span", { style: "color:#f87171;font-size:10px;" }, "failed")
              : null,
          ],
        ),
        el("img", {
          src: s.image,
          alt: cap,
          style: "display:block;width:100%;height:auto;background:#000;",
        }),
      ]),
    );
  }
}

function _openXRunDrawer(title) {
  let drawer = document.getElementById("x-run-drawer");
  if (!drawer) {
    drawer = el("div", {
      id: "x-run-drawer",
      style:
        "position:fixed;bottom:16px;right:16px;width:440px;max-height:65vh;background:#13131a;border:1px solid #2a2a38;border-radius:10px;box-shadow:0 12px 36px rgba(0,0,0,0.5);z-index:2100;display:flex;flex-direction:column;overflow:hidden;",
    });
    document.body.appendChild(drawer);
  }
  drawer.innerHTML = "";
  drawer.style.display = "flex";
  _xRunDrawerHistory = [];
  drawer.appendChild(
    el(
      "div",
      {
        style:
          "padding:10px 14px;border-bottom:1px solid #2a2a38;display:flex;align-items:center;gap:8px;",
      },
      [
        el("strong", { style: "color:#fff;font-size:13px;flex:1;" }, `🐦 ${title}`),
        el(
          "span",
          { id: "x-run-drawer-status", class: "muted", style: "font-size:11px;" },
          "Running…",
        ),
        el(
          "button",
          {
            title: "Copy every action (with links) to the clipboard",
            style:
              "background:transparent;border:1px solid #2a2a38;border-radius:5px;color:#6ea8ff;font-size:11px;cursor:pointer;padding:2px 7px;",
            "aria-label": "Copy action log",
            onclick: () => {
              const txt = _xRunDrawerHistory
                .map((h) => `[${h.t}] ${h.icon} ${h.text}${h.url ? "  " + h.url : ""}`)
                .join("\n");
              try {
                navigator.clipboard.writeText(txt);
                toast(`Copied ${_xRunDrawerHistory.length} actions`, "ok", 2500);
              } catch {
                toast("Copy failed", "err");
              }
            },
          },
          "⧉ Copy",
        ),
        el(
          "button",
          {
            title: "Visual replay — screenshots of each step",
            style:
              "background:transparent;border:1px solid #2a2a38;border-radius:5px;color:#a78bfa;font-size:11px;cursor:pointer;padding:2px 7px;",
            "aria-label": "Replay screenshots",
            onclick: () => _openRunReplayModal(_xRunCurrentRunId),
          },
          "📸 Replay",
        ),
        el(
          "button",
          {
            style:
              "background:transparent;border:none;color:#888;font-size:18px;cursor:pointer;padding:0 4px;",
            "aria-label": "Close progress panel",
            onclick: () => {
              drawer.style.display = "none";
              drawer.innerHTML = "";
              _teardownXRunBatchListener();
            },
          },
          "×",
        ),
      ],
    ),
  );
  drawer.appendChild(
    el("div", {
      id: "x-run-drawer-log",
      style:
        "padding:8px 14px;flex:1;overflow-y:auto;font-size:12px;font-family:monospace;color:#aaa;",
    }),
  );
}

function _xRunDrawerAppendLog(icon, text, url = null) {
  const t = new Date().toLocaleTimeString([], {
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
  });
  _xRunDrawerHistory.push({ icon, text, url, t });
  const log = document.getElementById("x-run-drawer-log");
  if (!log) return;
  const children = [
    el("span", { style: "opacity:.45;margin-right:6px;" }, `${t} ${icon}`),
    url
      ? el(
          "a",
          {
            href: url,
            target: "_blank",
            rel: "noopener noreferrer",
            style: "color:#6ea8ff;text-decoration:none;word-break:break-word;",
          },
          text,
        )
      : el("span", { style: "word-break:break-word;color:#aaa;" }, text),
  ];
  if (url)
    children.push(
      el(
        "a",
        {
          href: url,
          target: "_blank",
          rel: "noopener noreferrer",
          title: url,
          style: "color:#6ea8ff;text-decoration:none;margin-left:5px;",
        },
        "🔗",
      ),
    );
  log.appendChild(
    el(
      "div",
      { style: "padding:3px 0;border-bottom:1px solid #1a1a22;line-height:1.5;" },
      children,
    ),
  );
  log.scrollTop = log.scrollHeight;
}

function _xRunDrawerSetStatus(text) {
  const s = document.getElementById("x-run-drawer-status");
  if (s) s.textContent = text;
}

function _setupXRunBatchListener() {
  _teardownXRunBatchListener();
  _xRunBatchListener = (msg) => {
    if (!msg || !msg.nexusPanel) return;
    if (msg.type === "log")
      _xRunDrawerAppendLog(msg.data?.icon || "·", msg.data?.text || "", msg.data?.url || null);
    else if (msg.type === "status") _xRunDrawerSetStatus(String(msg.data || "").slice(0, 60));
  };
  try {
    chrome.runtime.onMessage.addListener(_xRunBatchListener);
  } catch {}
}

function _teardownXRunBatchListener() {
  if (_xRunBatchListener) {
    try {
      chrome.runtime.onMessage.removeListener(_xRunBatchListener);
    } catch {}
    _xRunBatchListener = null;
  }
}

// (Removed 2026-05-31: _openXActionInfoModal — X action cards now route through
// the shared openAutomationDetail() detail view, same as every other card type.)
