/**
 * Nexus Strategy Launcher — floating button + slide-up panel injected on every
 * supported domain (linkedin.com, x.com, cryptorank.io, lu.ma, gmail, telegram web).
 *
 * UX:
 *  - Small 48px circular button bottom-right, Shadow-DOM isolated
 *  - Click → panel opens with the list of strategies from GET /api/strategies
 *  - Context-relevant strategies (matching current hostname) appear at the top
 *  - Toggle-type: flip on/off inline
 *  - Launch-type: open deeplink to Telegram in a new tab
 *  - Action-type: trigger backend action with context = { url, postText?, authorName? }
 *
 * Pattern mirrors existing content-twitter.js / content-gmail.js Shadow DOM approach.
 */

(() => {
  // Prevent double-injection
  if (window.__nexusLauncherInjected) return;
  window.__nexusLauncherInjected = true;

  const LAUNCHER_ID = "nexus-launcher-root";
  const Z_INDEX = 2147483647;
  const currentHost = location.hostname.replace(/^www\./, "");

  // ── Strategy state cache (scoped to this tab) ────────────────────

  let strategiesCache = null;
  let panelOpen = false;
  let refreshTimer = null;

  // ── UI construction ──────────────────────────────────────────────

  function createLauncher() {
    if (document.getElementById(LAUNCHER_ID)) return;

    const host = document.createElement("div");
    host.id = LAUNCHER_ID;
    host.style.cssText = `
      position: fixed !important;
      bottom: 20px !important;
      right: 20px !important;
      z-index: ${Z_INDEX} !important;
      pointer-events: none !important;
    `;

    const shadow = host.attachShadow({ mode: "open" });

    shadow.innerHTML = `
      <style>
        * { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
        .fab {
          pointer-events: auto;
          width: 48px;
          height: 48px;
          border-radius: 50%;
          background: linear-gradient(135deg, #4ADE80, #5a4de8);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          border: none;
          transition: transform 0.2s;
          color: white;
          font-size: 20px;
          font-weight: bold;
        }
        .fab:hover {
          transform: scale(1.08);
        }
        .fab.active {
          background: linear-gradient(135deg, #5a4de8, #4236c7);
        }
        .panel {
          pointer-events: auto;
          position: absolute;
          bottom: 60px;
          right: 0;
          width: 340px;
          max-height: 520px;
          background: #0f0f11;
          border: 1px solid #2a2a38;
          border-radius: 12px;
          box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
          color: #e0e0e8;
          font-size: 13px;
          overflow: hidden;
          display: none;
          flex-direction: column;
          animation: slideUp 0.18s ease-out;
        }
        .panel.open { display: flex; }
        @keyframes slideUp {
          from { opacity: 0; transform: translateY(12px); }
          to { opacity: 1; transform: translateY(0); }
        }
        .panel-header {
          padding: 12px 14px 8px;
          border-bottom: 1px solid #1a1a22;
          display: flex;
          align-items: center;
          justify-content: space-between;
          flex-shrink: 0;
        }
        .panel-title {
          font-size: 14px;
          font-weight: 600;
          color: #e0e0e8;
        }
        .panel-domain {
          font-size: 10px;
          padding: 3px 8px;
          background: #1a1a22;
          border: 1px solid #2a2a38;
          border-radius: 8px;
          color: #888;
          text-transform: uppercase;
          letter-spacing: 0.04em;
        }
        .panel-close {
          background: none;
          border: none;
          color: #888;
          font-size: 18px;
          cursor: pointer;
          padding: 0 4px;
        }
        .panel-close:hover { color: #e0e0e8; }
        .panel-body {
          overflow-y: auto;
          flex: 1;
          padding: 8px;
        }
        .panel-body::-webkit-scrollbar { width: 6px; }
        .panel-body::-webkit-scrollbar-thumb { background: #2a2a38; border-radius: 3px; }
        .section-label {
          font-size: 9px;
          text-transform: uppercase;
          letter-spacing: 0.1em;
          color: #666;
          padding: 8px 6px 4px;
          font-weight: 600;
        }
        .strat {
          display: flex;
          align-items: center;
          gap: 10px;
          padding: 10px;
          background: #12121a;
          border: 1px solid #1f1f2e;
          border-radius: 8px;
          margin-bottom: 6px;
        }
        .strat:hover { background: #16161f; border-color: #2a2a38; }
        .strat.recommended { border-color: #4ADE80; background: #15122e; }
        .strat-icon {
          font-size: 20px;
          width: 28px;
          text-align: center;
          flex-shrink: 0;
        }
        .strat-body { flex: 1; min-width: 0; }
        .strat-name { font-size: 12px; font-weight: 600; color: #e0e0e8; }
        .strat-desc {
          font-size: 10px;
          color: #888;
          line-height: 1.35;
          margin-top: 2px;
          display: -webkit-box;
          -webkit-line-clamp: 2;
          -webkit-box-orient: vertical;
          overflow: hidden;
        }
        .strat-status {
          font-size: 9px;
          color: #666;
          margin-top: 3px;
        }
        .strat-action { flex-shrink: 0; }
        .strat-btn {
          padding: 5px 10px;
          font-size: 11px;
          font-weight: 600;
          border-radius: 5px;
          border: none;
          cursor: pointer;
          font-family: inherit;
        }
        .strat-btn.primary { background: #4ADE80; color: white; }
        .strat-btn.primary:hover { background: #6EE79B; }
        .strat-btn.secondary { background: #1a1a22; color: #a0a0b0; border: 1px solid #2a2a38; }
        .strat-btn.secondary:hover { background: #22222e; }
        .strat-toggle {
          display: inline-block;
          width: 34px;
          height: 18px;
          background: #333;
          border-radius: 9px;
          position: relative;
          cursor: pointer;
          transition: background 0.15s;
        }
        .strat-toggle::after {
          content: "";
          position: absolute;
          top: 2px;
          left: 2px;
          width: 14px;
          height: 14px;
          background: #fff;
          border-radius: 50%;
          transition: left 0.15s;
        }
        .strat-toggle.on { background: #4ade80; }
        .strat-toggle.on::after { left: 18px; }
        .panel-footer {
          padding: 8px 12px;
          border-top: 1px solid #1a1a22;
          font-size: 10px;
          color: #666;
          text-align: center;
          flex-shrink: 0;
        }
        .panel-toast {
          position: absolute;
          bottom: 40px;
          left: 12px;
          right: 12px;
          padding: 8px 12px;
          border-radius: 6px;
          font-size: 11px;
          text-align: center;
          display: none;
        }
        .panel-toast.ok { background: #1a3a1a; color: #4ade80; border: 1px solid #2a5a2a; }
        .panel-toast.err { background: #3a1a1a; color: #f87171; border: 1px solid #5a2a2a; }
        .empty {
          text-align: center;
          padding: 32px 12px;
          color: #666;
          font-size: 12px;
        }
        .spinner {
          display: inline-block;
          width: 12px;
          height: 12px;
          border: 2px solid rgba(255,255,255,0.2);
          border-top-color: #4ADE80;
          border-radius: 50%;
          animation: spin 0.6s linear infinite;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
      </style>

      <div class="panel" id="panel">
        <div class="panel-header">
          <span class="panel-title">Nexus Strategies</span>
          <span class="panel-domain" id="panel-domain"></span>
          <button class="panel-close" id="panel-close">\u00d7</button>
        </div>
        <div class="panel-body" id="panel-body">
          <div class="empty">Loading...</div>
        </div>
        <div class="panel-footer">Open the extension popup for full dashboard</div>
        <div class="panel-toast" id="panel-toast"></div>
      </div>
      <button class="fab" id="fab" title="Nexus Strategies">N</button>
    `;

    document.body.appendChild(host);

    const fab = shadow.getElementById("fab");
    const panel = shadow.getElementById("panel");
    const closeBtn = shadow.getElementById("panel-close");
    const domainEl = shadow.getElementById("panel-domain");

    domainEl.textContent = currentHost;

    fab.addEventListener("click", async () => {
      panelOpen = !panelOpen;
      panel.classList.toggle("open", panelOpen);
      fab.classList.toggle("active", panelOpen);
      if (panelOpen) {
        await renderPanel();
        startAutoRefresh();
      } else {
        stopAutoRefresh();
      }
    });

    closeBtn.addEventListener("click", () => {
      panelOpen = false;
      panel.classList.remove("open");
      fab.classList.remove("active");
      stopAutoRefresh();
    });

    // Close on outside click
    document.addEventListener("click", (e) => {
      if (!panelOpen) return;
      if (e.composedPath && e.composedPath().includes(host)) return;
      panelOpen = false;
      panel.classList.remove("open");
      fab.classList.remove("active");
      stopAutoRefresh();
    });

    return { shadow, fab, panel };
  }

  function startAutoRefresh() {
    if (refreshTimer) return;
    refreshTimer = setInterval(() => {
      if (panelOpen) renderPanel();
    }, 30000);
  }
  function stopAutoRefresh() {
    if (refreshTimer) { clearInterval(refreshTimer); refreshTimer = null; }
  }

  // ── Data loading + render ───────────────────────────────────────

  async function fetchStrategies() {
    try {
      return await chrome.runtime.sendMessage({ action: "strategy-list" });
    } catch (err) {
      return { error: err.message };
    }
  }

  function isRelevantToCurrentHost(strategy) {
    if (!strategy.relevantDomains || strategy.relevantDomains.length === 0) return false;
    return strategy.relevantDomains.some(d => currentHost.endsWith(d.replace(/^\./, "")));
  }

  async function renderPanel() {
    const host = document.getElementById(LAUNCHER_ID);
    if (!host) return;
    const shadow = host.shadowRoot;
    const body = shadow.getElementById("panel-body");

    const data = await fetchStrategies();
    strategiesCache = data;

    if (data?.error) {
      body.innerHTML = `<div class="empty">Error: ${escapeHtml(data.error)}</div>`;
      return;
    }

    const strategies = data?.strategies || [];
    const relevant = strategies.filter(isRelevantToCurrentHost);
    const other = strategies.filter(s => !isRelevantToCurrentHost(s));

    // ── ICP scoring section (only on company pages) ─────────────
    const icpHtml = isCompanyPage() ? renderIcpSectionPlaceholder() : "";

    let html = icpHtml;
    if (relevant.length > 0) {
      html += `<div class="section-label">For this page</div>`;
      for (const s of relevant) html += renderStrategyRow(s, true);
    }
    if (other.length > 0) {
      html += `<div class="section-label">All strategies</div>`;
      for (const s of other) html += renderStrategyRow(s, false);
    }
    if (!icpHtml && strategies.length === 0) {
      html = `<div class="empty">No strategies available</div>`;
    }

    body.innerHTML = html;

    // Wire ICP section if present
    if (icpHtml) wireIcpSection(shadow);

    // Wire up strategy handlers
    for (const s of strategies) {
      if (s.type === "toggle") {
        const el = shadow.getElementById(`strat-toggle-${s.id}`);
        if (el) el.addEventListener("click", () => onToggle(s.id, !s.status?.running));
      } else if (s.type === "launch") {
        const el = shadow.getElementById(`strat-launch-${s.id}`);
        if (el) el.addEventListener("click", () => onLaunch(s.id));
      } else if (s.type === "action") {
        const el = shadow.getElementById(`strat-action-${s.id}`);
        if (el) el.addEventListener("click", () => onAction(s.id));
      }
    }
  }

  // ── ICP: in-page scoring for LinkedIn/X company pages ──────────

  const ICP_BAND_COLOR = { bin: "#f87171", inbound: "#a0a0b0", outbound: "#60a5fa", abm: "#4ade80" };
  const ICP_BAND_LABEL = { bin: "Bin", inbound: "Inbound only", outbound: "Outbound", abm: "ABM" };

  function isCompanyPage() {
    // LinkedIn: /company/<slug>
    if (currentHost.includes("linkedin.com") && /^\/company\//.test(location.pathname)) return true;
    // X/Twitter profile (treat as "company" too for adhoc scoring)
    if ((currentHost.includes("x.com") || currentHost.includes("twitter.com"))
        && /^\/[a-zA-Z0-9_]+\/?$/.test(location.pathname)) return true;
    return false;
  }

  // Extraction is expensive: document.body.innerText forces a full layout + text
  // serialization of the page (routinely 50-200KB on a LinkedIn company page) and
  // inferCryptoHintsFromText then runs ~40 regexes over it. The panel re-renders on a
  // 30s timer while open, and a settled company page yields an identical result every
  // time, so memoize per URL. The TTL keeps late-loading page content from being
  // pinned forever behind a snapshot taken while the page was still rendering.
  const CTX_CACHE_TTL_MS = 120000;
  let ctxCache = { url: null, at: 0, ctx: null };

  function extractCompanyContext() {
    const now = Date.now();
    if (ctxCache.ctx && ctxCache.url === location.href && now - ctxCache.at < CTX_CACHE_TTL_MS) {
      // Callers mutate what they get back (delete signals.url, ...), so keep ours pristine.
      return { ...ctxCache.ctx };
    }

    const ctx = { url: location.href };

    if (currentHost.includes("linkedin.com") && /^\/company\//.test(location.pathname)) {
      // Company slug from URL
      const slugMatch = location.pathname.match(/^\/company\/([^\/?#]+)/);
      ctx.linkedin_slug = slugMatch ? slugMatch[1] : null;

      const h1 = document.querySelector("h1");
      if (h1) ctx.name = h1.innerText.trim().split("\n")[0];

      const aboutText = document.body.innerText || "";

      // Industry → crypto_category (best-effort keyword match)
      const indMatch = aboutText.match(/Industry[\s\n]+([A-Za-z][A-Za-z&,\-\s]{2,80})/);
      if (indMatch) ctx.sector = indMatch[1].trim().split("\n")[0];

      Object.assign(ctx, inferCryptoHintsFromText(aboutText));
    } else if (currentHost.includes("x.com") || currentHost.includes("twitter.com")) {
      const h = document.querySelector('[data-testid="UserName"]');
      ctx.name = h ? h.innerText.trim().split("\n")[0] : null;
      ctx.twitter_handle = location.pathname.replace(/^\//, "").replace(/\/$/, "");

      // X bio blob — pull crypto hints from visible profile text
      const bioEl = document.querySelector('[data-testid="UserDescription"]')
        || document.querySelector('[data-testid="UserProfileHeader_Items"]');
      const bioText = bioEl ? bioEl.innerText : (document.body.innerText || "").slice(0, 2000);
      Object.assign(ctx, inferCryptoHintsFromText(bioText));
    }

    ctxCache = { url: ctx.url, at: now, ctx };
    return { ...ctx };
  }

  // Best-effort crypto-signal extraction from a blob of visible page text.
  // Intentionally conservative — we only emit a field when a confident keyword match fires.
  function inferCryptoHintsFromText(text) {
    const out = {};
    if (!text) return out;
    const lower = text.toLowerCase();

    // Ecosystem / chain
    const chainRegexes = [
      [/\b(ethereum|\beth\b|\berc-20|\berc20)\b/, "ETH"],
      [/\b(solana|\bsol\b)\b/, "SOL"],
      [/\b(bitcoin|\bbtc\b)\b/, "BTC"],
      [/\bbase\b/, "BASE"],
      [/\b(arbitrum|\barb\b)\b/, "ARB"],
      [/\b(optimism|\bop\s+mainnet)\b/, "OP"],
      [/\bavalanche|\bavax\b/, "AVAX"],
      [/\bpolygon|\bmatic\b/, "MATIC"],
      [/\bbnb chain|\bbsc\b|binance smart chain/, "BNB"],
      [/\bsui\b/, "SUI"],
      [/\baptos\b|\bapt\b/, "APT"],
      [/\bcosmos\b|\batom\b/, "ATOM"],
      [/\bnear protocol|\bnear\b/, "NEAR"],
      [/\bton blockchain|\bton\b/, "TON"],
    ];
    const chainHits = chainRegexes.filter(([re]) => re.test(lower)).map(([, c]) => c);
    if (chainHits.length === 1) out.ecosystem = chainHits[0];
    else if (chainHits.length > 1) out.ecosystem = "multichain";

    // Crypto category (first match wins)
    const catRegexes = [
      [/\b(l1|layer.?1|monolithic blockchain)\b/, "l1"],
      [/\b(l2|layer.?2|rollup|zk.?rollup|optimistic rollup)\b/, "l2"],
      [/\b(perp|perpetual|futures|derivatives)\b/, "perps"],
      [/\b(dex|amm|orderbook|swap)\b/, "dex"],
      [/\b(lend|borrow|money market)\b/, "lending"],
      [/\b(stablecoin|pegged asset)\b/, "stablecoin"],
      [/\b(yield|vault|lst|liquid staking)\b/, "yield"],
      [/\b(bridge|cross.?chain|interchain)\b/, "bridge"],
      [/\bwallet|account abstraction|smart wallet\b/, "wallet"],
      [/\b(oracle|price feed)\b/, "oracle"],
      [/\b(rpc|node provider|indexer)\b/, "rpc"],
      [/\b(sdk|devtool|developer tool)\b/, "devtools"],
      [/\b(audit|security firm|smart contract security)\b/, "audit"],
      [/\bcustody|custodian|institutional custody\b/, "custody"],
      [/\blaunchpad|ido|incubator|accelerator\b/, "launchpad"],
      [/\bnft|non.?fungible\b/, "nft"],
      [/\bgamefi|gaming|play.?to.?earn\b/, "gaming"],
      [/\bsocialfi|decentralized social\b/, "social"],
      [/\bdao|governance\b/, "dao"],
      [/\banalytics|on-chain data|market data\b/, "analytics"],
      [/\bresearch|newsletter|podcast|media\b/, "media"],
      [/\bon.?ramp|off.?ramp|payment|stablecoin rail\b/, "payments"],
      [/\brwa|real.?world asset|tokeniz(?:ed|ation)\b/, "rwa"],
      [/\bai.?agent|ai x crypto\b/, "ai"],
    ];
    for (const [re, cat] of catRegexes) {
      if (re.test(lower)) { out.crypto_category = cat; break; }
    }

    // TVL hint ("$X in TVL", "Total Value Locked: $X")
    const tvlMatch = text.match(/(\$|USD\s*)([\d,.]+)\s*(b|m|bn|mn|million|billion)?\s*(?:tvl|total value locked|in (?:assets|deposits))/i);
    if (tvlMatch) {
      const n = Number(tvlMatch[2].replace(/,/g, ""));
      const unit = (tvlMatch[3] || "").toLowerCase();
      const usd = unit.startsWith("b") ? n * 1e9
                : unit.startsWith("m") ? n * 1e6
                : n;
      if (usd >= 1e9) out.tvl_band = "1b+";
      else if (usd >= 1e8) out.tvl_band = "100m-1b";
      else if (usd >= 1e7) out.tvl_band = "10-100m";
      else if (usd >= 1e6) out.tvl_band = "1-10m";
      else if (usd > 0)    out.tvl_band = "<1m";
    }

    // Has-token hint (presence of $TICKER in text, or "token launched", etc.)
    if (/\$[A-Z]{2,6}\b/.test(text) || /token launch|\btoken holders\b|\btge\b/i.test(text)) {
      out.has_token = 1;
    }

    // VC backing hint (tier-1 names)
    if (/\b(a16z|andreessen horowitz|paradigm|multicoin|polychain|sequoia|coinbase ventures|pantera|dragonfly|electric capital|variant|delphi|framework ventures)\b/i.test(text)) {
      out.backed_by_tier_1 = 1;
    }

    // Dormant / rugged hint
    if (/rug|exit scam|abandoned|no longer active|project discontinued/i.test(text)) {
      out.is_rugged = 1;
    }

    // Differentiation notes — short first sentence of bio if present
    const firstSentence = text.split(/[.!?]\s/)[0];
    if (firstSentence && firstSentence.length >= 15 && firstSentence.length <= 140) {
      out.differentiation_notes = firstSentence.trim();
    }

    return out;
  }

  function renderIcpSectionPlaceholder() {
    return `
      <div class="section-label">🎯 ICP Score</div>
      <div id="icp-card" style="
        padding: 12px;
        background: #12121a;
        border: 1px solid #2a2a38;
        border-radius: 8px;
        margin-bottom: 8px;
      ">
        <div style="font-size:11px;color:#888;margin-bottom:8px;" id="icp-ctx-line">Scanning page…</div>
        <div id="icp-score-display" style="display:none;">
          <div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;">
            <div id="icp-score-num" style="font-size:28px;font-weight:700;color:#e0e0e8;"></div>
            <div style="color:#888;font-size:11px;">/ 10</div>
            <div id="icp-band-badge" style="
              font-size:10px;padding:3px 8px;border-radius:10px;
              text-transform:uppercase;font-weight:600;margin-left:auto;
            "></div>
          </div>
          <div id="icp-blurb" style="font-size:11px;color:#a0a0b0;line-height:1.4;"></div>
          <div id="icp-disqualified" style="
            display:none;font-size:10px;color:#f87171;margin-top:6px;
          "></div>
        </div>
        <div style="display:flex;gap:6px;margin-top:8px;">
          <button class="strat-btn primary" id="icp-score-btn" style="flex:1;">Score now</button>
          <button class="strat-btn secondary" id="icp-save-btn" style="flex:1;display:none;">Save to CRM</button>
        </div>
      </div>
    `;
  }

  function wireIcpSection(shadow) {
    const ctx = extractCompanyContext();
    const ctxLine = shadow.getElementById("icp-ctx-line");
    if (ctx.name) {
      ctxLine.textContent = `${ctx.name}${ctx.sector ? " · " + ctx.sector : ""}${ctx.country ? " · " + ctx.country : ""}`;
    } else {
      ctxLine.textContent = "Couldn't extract company name from this page.";
    }

    const scoreBtn = shadow.getElementById("icp-score-btn");
    const saveBtn = shadow.getElementById("icp-save-btn");
    const displayWrap = shadow.getElementById("icp-score-display");
    const scoreNum = shadow.getElementById("icp-score-num");
    const bandBadge = shadow.getElementById("icp-band-badge");
    const blurb = shadow.getElementById("icp-blurb");
    const dqLine = shadow.getElementById("icp-disqualified");

    let lastResult = null;

    scoreBtn.addEventListener("click", async () => {
      scoreBtn.textContent = "Scoring…";
      scoreBtn.disabled = true;
      const adhoc = { ...ctx };
      delete adhoc.url; delete adhoc.linkedin_slug; delete adhoc.twitter_handle;

      try {
        const r = await chrome.runtime.sendMessage({ action: "icp-score-one", body: { adhoc } });
        if (r?.error) { showToast(`❌ ${r.error}`, "err"); scoreBtn.textContent = "Score now"; scoreBtn.disabled = false; return; }
        lastResult = r;
        displayWrap.style.display = "block";
        scoreNum.textContent = Number(r.score).toFixed(1);
        const band = r.band || "bin";
        bandBadge.textContent = ICP_BAND_LABEL[band];
        bandBadge.style.background = ICP_BAND_COLOR[band];
        bandBadge.style.color = "#0a0a0f";
        blurb.textContent = (r.band_copy && r.band_copy.blurb) || "";
        if (r.declarative?.disqualifiedBy?.length) {
          dqLine.style.display = "block";
          dqLine.textContent = `Killed by: ${r.declarative.disqualifiedBy.join(", ")}`;
        } else {
          dqLine.style.display = "none";
        }
        saveBtn.style.display = "block";
        scoreBtn.textContent = "Rescore";
        scoreBtn.disabled = false;
      } catch (err) {
        showToast(`❌ ${err.message}`, "err");
        scoreBtn.textContent = "Score now";
        scoreBtn.disabled = false;
      }
    });

    saveBtn.textContent = "Open in dashboard";
    saveBtn.addEventListener("click", async () => {
      // Open the dashboard ICP wizard and stash the extracted signals for pre-fill.
      try {
        await chrome.storage.local.set({
          _pendingIcpWizard: { ctx, result: lastResult, capturedAt: Date.now() },
        });
      } catch { /* storage permission may be missing — not fatal */ }
      try {
        const url = chrome.runtime.getURL("dashboard.html#icp/score");
        window.open(url, "_blank", "noopener");
      } catch (err) {
        showToast(`❌ ${err.message}`, "err");
      }
    });
  }

  function renderStrategyRow(s, highlighted) {
    const statusText = s.status?.running
      ? "\u{1f7e2} Running"
      : s.status?.needsSession
        ? "\u26a0\ufe0f Needs session"
        : "\u26aa Idle";

    let actionHtml = "";
    if (s.type === "toggle") {
      actionHtml = `<div class="strat-action"><div class="strat-toggle${s.status?.running ? " on" : ""}" id="strat-toggle-${escapeAttr(s.id)}"></div></div>`;
    } else if (s.type === "launch") {
      actionHtml = `<div class="strat-action"><button class="strat-btn primary" id="strat-launch-${escapeAttr(s.id)}">Launch</button></div>`;
    } else if (s.type === "action") {
      actionHtml = `<div class="strat-action"><button class="strat-btn secondary" id="strat-action-${escapeAttr(s.id)}">Run</button></div>`;
    }

    return `
      <div class="strat${highlighted ? " recommended" : ""}">
        <div class="strat-icon">${s.icon || "\u2022"}</div>
        <div class="strat-body">
          <div class="strat-name">${escapeHtml(s.name)}</div>
          <div class="strat-desc">${escapeHtml(s.description)}</div>
          <div class="strat-status">${statusText}</div>
        </div>
        ${actionHtml}
      </div>
    `;
  }

  // ── Action handlers ─────────────────────────────────────────────

  async function onToggle(strategyId, enable) {
    showToast("Updating...", "ok");
    try {
      const result = await chrome.runtime.sendMessage({
        action: "strategy-toggle",
        strategyId,
        enabled: enable,
      });
      if (result?.error) {
        showToast(`\u274c ${result.error}`, "err");
      } else {
        showToast(`\u2705 ${enable ? "Enabled" : "Disabled"}`, "ok");
        await renderPanel();
      }
    } catch (err) {
      showToast(`\u274c ${err.message}`, "err");
    }
  }

  async function onLaunch(strategyId) {
    showToast("Opening Telegram...", "ok");
    try {
      const result = await chrome.runtime.sendMessage({
        action: "strategy-deeplink",
        strategyId,
      });
      if (result?.deeplink) {
        window.open(result.deeplink, "_blank", "noopener");
        showToast(`\u2705 Opened in new tab`, "ok");
      } else {
        showToast(`\u274c ${result?.error || "no deeplink"}`, "err");
      }
    } catch (err) {
      showToast(`\u274c ${err.message}`, "err");
    }
  }

  async function onAction(strategyId) {
    showToast("Running...", "ok");

    // Build context from current page
    const context = { url: location.href };

    // For AI comment strategy, try to extract post text from current page
    if (strategyId === "ai_comment_current_post") {
      context.postText = extractCurrentPostText();
      context.authorName = extractCurrentAuthorName();
      context.platform = currentHost.includes("linkedin") ? "linkedin"
                       : currentHost.includes("x.com") || currentHost.includes("twitter") ? "twitter"
                       : "unknown";
      if (!context.postText) {
        showToast("\u274c Couldn't find a post on this page", "err");
        return;
      }
    }

    // For save-to-CRM, try to extract profile name
    if (strategyId === "save_profile_to_crm") {
      context.name = extractProfileName();
      context.platform = currentHost.includes("linkedin") ? "linkedin"
                       : currentHost.includes("x.com") || currentHost.includes("twitter") ? "twitter"
                       : "unknown";
    }

    try {
      const result = await chrome.runtime.sendMessage({
        action: "strategy-action",
        strategyId,
        context,
      });
      if (result?.ok) {
        if (result.comment) {
          try {
            await navigator.clipboard.writeText(result.comment);
            showToast(`\u2705 Comment copied to clipboard`, "ok");
          } catch {
            showToast(`\u2705 ${result.comment.slice(0, 40)}...`, "ok");
          }
        } else if (result.contactId) {
          showToast(result.deduped ? `\u2705 Already in CRM` : `\u2705 Saved to CRM`, "ok");
        } else {
          showToast(`\u2705 Done`, "ok");
        }
      } else {
        showToast(`\u274c ${result?.error || "failed"}`, "err");
      }
    } catch (err) {
      showToast(`\u274c ${err.message}`, "err");
    }
  }

  // ── Page content extractors (best-effort, per platform) ───────

  function extractCurrentPostText() {
    // LinkedIn feed post
    const liPost = document.querySelector('.feed-shared-update-v2__description, .feed-shared-text__text-view, [data-test-id="main-feed-activity-card__commentary"]');
    if (liPost) return liPost.innerText.trim().slice(0, 2000);

    // X/Twitter tweet
    const tweet = document.querySelector('[data-testid="tweetText"]');
    if (tweet) return tweet.innerText.trim().slice(0, 2000);

    return null;
  }

  function extractCurrentAuthorName() {
    // LinkedIn
    const liAuthor = document.querySelector('.feed-shared-actor__name, .update-components-actor__name, .feed-shared-actor__title');
    if (liAuthor) return liAuthor.innerText.trim().split("\n")[0];

    // X
    const xAuthor = document.querySelector('[data-testid="User-Name"] a');
    if (xAuthor) return xAuthor.innerText.trim().split("\n")[0];

    return null;
  }

  function extractProfileName() {
    // LinkedIn profile page
    if (currentHost.includes("linkedin") && location.pathname.startsWith("/in/")) {
      const h = document.querySelector("h1");
      if (h) return h.innerText.trim();
    }
    // X profile page
    if (currentHost.includes("x.com") || currentHost.includes("twitter")) {
      const h = document.querySelector('[data-testid="UserName"]');
      if (h) return h.innerText.trim().split("\n")[0];
    }
    return null;
  }

  // ── Helpers ─────────────────────────────────────────────────────

  function showToast(text, kind = "ok") {
    const host = document.getElementById(LAUNCHER_ID);
    if (!host) return;
    const toast = host.shadowRoot.getElementById("panel-toast");
    if (!toast) return;
    toast.textContent = text;
    toast.className = `panel-toast ${kind}`;
    toast.style.display = "block";
    setTimeout(() => { toast.style.display = "none"; }, 3500);
  }

  function escapeHtml(s) {
    return String(s ?? "")
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  function escapeAttr(s) {
    return String(s ?? "").replace(/[^a-zA-Z0-9_-]/g, "_");
  }

  // ── Init ─────────────────────────────────────────────────────────

  // Platform panels (LinkedIn / Twitter / Telegram / Gmail content scripts) own their
  // own floating button on those domains. To avoid two FABs stacked on the same page,
  // suppress the launcher when any platform panel is detected.
  // Phase 2 will fold the launcher's strategy list INTO each platform panel as a tab.
  const PLATFORM_PANEL_IDS = [
    "nexus-connect-panel",   // LinkedIn
    "nexus-twitter-panel",   // Twitter / X
    "nexus-telegram-panel",  // Telegram Web
    "nexus-gmail-panel",     // Gmail
  ];

  function platformPanelPresent() {
    return PLATFORM_PANEL_IDS.some(id => document.getElementById(id));
  }

  function maybeCreateLauncher() {
    if (platformPanelPresent()) return; // platform owns the FAB on this domain
    createLauncher();
  }

  function init() {
    // Wait briefly so platform panels (which also inject on DOMContentLoaded) get a
    // chance to register before we check. Avoids a brief flash of two FABs.
    const start = () => setTimeout(maybeCreateLauncher, 600);
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", start, { once: true });
    } else {
      start();
    }

    // Passive ICP signal harvest — if the server has a queued scrape for this page,
    // scrape silently and submit. No UI. Runs after the page settles.
    setTimeout(maybeHarvestForIcp, 3500);
  }

  // ── Passive LinkedIn-side ICP signal harvester ─────────────────
  // Triggered on page load. Quiet, best-effort. Skips on non-company pages.
  async function maybeHarvestForIcp() {
    if (!isCompanyPage()) return;
    try {
      const resp = await chrome.runtime.sendMessage({ action: "icp-scrape-next", url: location.href });
      if (!resp || resp.error || !resp.taskId) return;           // nothing queued, or error
      const signals = extractCompanyContext();
      delete signals.url; delete signals.linkedin_slug; delete signals.twitter_handle;
      await chrome.runtime.sendMessage({
        action: "icp-scrape-result",
        taskId: resp.taskId,
        body: { signals },
      });
    } catch (err) {
      // Silent — this is a background harvest, never show the user.
    }
  }

  init();
})();
