// dashboard-k-command-palette.js — extracted verbatim from dashboard.js (pure move).

// ── ⌘K COMMAND PALETTE ─────────────────────────────────────────
// Global keyboard shortcut + modal overlay that searches across:
//   pages · cron jobs · CRM contacts · CRM companies · active campaigns · deals · signals
// Auto-attaches a Cmd/Ctrl+K listener once on first render of the dashboard.

let _cmdPaletteWired = false;
function wireCommandPaletteShortcut() {
  if (_cmdPaletteWired) return;
  _cmdPaletteWired = true;
  // Cmd/Ctrl+K opens the palette from ANY route — uses the persistent
  // #cmdk-overlay element so it's not coupled to renderLiveMap.
  // Cmd/Ctrl+1..7 jumps to V3 primary sections (Today / Inbox / Pipeline /
  // Find / Automate / Monitor / Earn). Only fires when V3 is the active
  // sidebar AND the user isn't typing in an input/textarea/contenteditable.
  document.addEventListener("keydown", (e) => {
    if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
      e.preventDefault();
      if (liveMapState.commandPalette.open) closeCommandPalette();
      else openCommandPalette();
      return;
    }
    if (e.key === "Escape" && liveMapState.commandPalette.open) {
      e.preventDefault();
      closeCommandPalette();
      return;
    }
    // V3 primary section shortcuts: Cmd/Ctrl+1..7
    if ((e.metaKey || e.ctrlKey) && /^[1-7]$/.test(e.key) && !e.shiftKey && !e.altKey) {
      // Don't hijack typing
      const t = e.target;
      const tag = t && t.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || (t && t.isContentEditable)) return;
      // Only when V3 sidebar is active
      const sb = document.getElementById("sidebar");
      if (!sb || !sb.classList.contains("sidebar-v3")) return;
      // Resolve against what is actually RENDERED, not V3_SECTIONS: a gated
      // section (e.g. Clients) may be filtered out, and indexing the raw array
      // would fire a different room than the "⌘+N" tooltip on screen promises.
      const item = sb.querySelectorAll(".nav-item-primary")[Number(e.key) - 1];
      if (!item || !item.dataset.route) return;
      e.preventDefault();
      navigate(item.dataset.route);
      logTelemetry("nav", "v3_shortcut", { key: e.key, section: item.dataset.section });
    }
  });
}

function openCommandPalette() {
  const overlay = $("#cmdk-overlay");
  if (!overlay) return;
  liveMapState.commandPalette.open = true;
  liveMapState.commandPalette.query = "";
  liveMapState.commandPalette.results = [];
  liveMapState.commandPalette.selectedIndex = 0;
  overlay.hidden = false;

  const input = $("#cmdk-input");
  const results = $("#cmdk-results");
  if (input) {
    input.value = "";
    input.oninput = (e) => {
      const cp = liveMapState.commandPalette;
      cp.query = e.target.value;
      cp.selectedIndex = 0;
      if (cp.searchHandle) clearTimeout(cp.searchHandle);
      // Show local matches synchronously, then layer remote on top after debounce.
      cp.results = runLocalCommandSearch(cp.query);
      renderCommandPaletteResults();
      cp.searchHandle = setTimeout(async () => {
        cp.results = await runCommandPaletteSearch(cp.query);
        renderCommandPaletteResults();
      }, 200);
    };
    input.onkeydown = (e) => {
      const cp = liveMapState.commandPalette;
      if (e.key === "ArrowDown") {
        e.preventDefault();
        cp.selectedIndex = Math.min(cp.selectedIndex + 1, Math.max(cp.results.length - 1, 0));
        renderCommandPaletteResults();
      } else if (e.key === "ArrowUp") {
        e.preventDefault();
        cp.selectedIndex = Math.max(cp.selectedIndex - 1, 0);
        renderCommandPaletteResults();
      } else if (e.key === "Enter") {
        e.preventDefault();
        const r = cp.results[cp.selectedIndex];
        if (r) executeCommandResult(r);
      }
    };
    setTimeout(() => input.focus(), 30);
  }
  if (results) {
    results.innerHTML = "";
    results.appendChild(renderCommandPaletteResultsContent("", [], 0));
  }

  // Backdrop click closes
  overlay.onclick = (e) => {
    if (e.target === overlay) closeCommandPalette();
  };
}

function closeCommandPalette() {
  const overlay = $("#cmdk-overlay");
  if (overlay) overlay.hidden = true;
  liveMapState.commandPalette.open = false;
  if (liveMapState.commandPalette.searchHandle) {
    clearTimeout(liveMapState.commandPalette.searchHandle);
    liveMapState.commandPalette.searchHandle = null;
  }
}

// Re-render only the #cmdk-results list — preserves input focus.
function renderCommandPaletteResults() {
  const wrap = $("#cmdk-results");
  if (!wrap) return;
  const cp = liveMapState.commandPalette;
  wrap.innerHTML = "";
  wrap.appendChild(renderCommandPaletteResultsContent(cp.query, cp.results, cp.selectedIndex));
}

function renderCommandPaletteResultsContent(query, results, selectedIdx) {
  if (!query || query.length < 1) {
    const recent = getPaletteRecent();
    if (recent.length === 0) {
      return el(
        "div",
        { class: "cmdk-empty" },
        "Start typing to search contacts, companies, deals, jobs, pages, and active campaigns.",
      );
    }
    const wrap = el("div", {});
    wrap.appendChild(el("div", { class: "cmdk-section-label" }, "Recent"));
    recent.forEach((r, i) => wrap.appendChild(buildPaletteRow(r, i, selectedIdx)));
    return wrap;
  }
  if (results.length === 0) {
    return el("div", { class: "cmdk-empty" }, "No results.");
  }

  const wrap = el("div", {});
  results.forEach((r, i) => wrap.appendChild(buildPaletteRow(r, i, selectedIdx)));
  return wrap;
}

const _PALETTE_KIND_ICON = {
  page: "📄",
  job: "⚙",
  contact: "👤",
  company: "🏢",
  deal: "💰",
  campaign: "📣",
  signal: "🔥",
};

function buildPaletteRow(r, i, selectedIdx) {
  const isSel = i === selectedIdx;
  return el(
    "div",
    {
      class: `cmdk-row${isSel ? " active" : ""}`,
      onmouseenter: () => {
        const cp = liveMapState.commandPalette;
        if (cp.selectedIndex !== i) {
          cp.selectedIndex = i;
          renderCommandPaletteResults();
        }
      },
      onclick: () => executeCommandResult(r),
    },
    [
      el("span", { class: "cmdk-icon" }, _PALETTE_KIND_ICON[r.kind] || "•"),
      el("div", { class: "cmdk-text" }, [
        el("div", { class: "cmdk-label" }, r.label || ""),
        r.detail ? el("div", { class: "cmdk-sublabel" }, r.detail) : null,
      ]),
      el("span", { class: "cmdk-meta" }, r.kind),
    ],
  );
}

// LRU recent list — last 8 entities the user activated. Stored in localStorage.
const _PALETTE_RECENT_KEY = "nexus.cmdk.recent";
function getPaletteRecent() {
  try {
    const raw = localStorage.getItem(_PALETTE_RECENT_KEY);
    if (!raw) return [];
    const arr = JSON.parse(raw);
    return Array.isArray(arr) ? arr : [];
  } catch {
    return [];
  }
}
function pushPaletteRecent(item) {
  if (!item || !item.kind || !item.id) return;
  try {
    const list = getPaletteRecent().filter(
      (x) => !(x.kind === item.kind && String(x.id) === String(item.id)),
    );
    list.unshift({ kind: item.kind, id: item.id, label: item.label, detail: item.detail });
    localStorage.setItem(_PALETTE_RECENT_KEY, JSON.stringify(list.slice(0, 8)));
  } catch {
    /* ignore quota / disabled storage */
  }
}

// Execute the chosen result — navigate, open drawer, run job, etc.
function executeCommandResult(r) {
  closeCommandPalette();
  pushPaletteRecent(r);
  if (r.kind === "page" && r.route) navigate(r.route);
  else if (r.kind === "job" && r.action === "trigger") {
    api("admin-trigger", { body: { job: r.id } }).then((resp) =>
      resp?.error ? toast(`${resp.error}`, "err") : toast(`▶ ${r.id} triggered`, "ok"),
    );
  } else if (r.kind === "contact") {
    showContactDrawer(r.id);
  } else if (r.kind === "company") {
    if (r.row) showCompanyDrawer(r.row);
    else openCompanyDrawerById(r.id);
  } else if (r.kind === "deal") navigate("deals");
  else if (r.kind === "campaign")
    navigate(r.type && r.id ? `campaigns/${r.type}/${r.id}` : "campaigns");
  else if (r.kind === "signal") navigate("signals");
  // wizard launcher — universal "new X" entry. Optionally prefill the wizard.
  else if (r.kind === "wizard") {
    if (r.prefill) {
      chrome.storage.local.set({ _wizardPrefill: r.prefill }).catch(() => {});
    }
    if (r.action) {
      try {
        r.action();
      } catch {}
    } else navigate(r.route || "campaigns");
    logTelemetry("campaign", "palette_launch", { label: r.label });
  }
  // Bookmarks: route to the underlying entity per its type. Falls back to
  // the bookmarks page if we can't resolve the type-specific surface.
  else if (r.kind === "bookmark") {
    if (r.bookmarkType === "profile" && typeof openPeopleDetail === "function")
      openPeopleDetail(r.id);
    else if (r.bookmarkType === "company") openCompanyDrawerById(r.id);
    else if (r.bookmarkType === "opportunity") navigate("bookmarks");
    else navigate("bookmarks");
  }
}

// Local-only search results (instant, no API). Pages + jobs + active campaigns + insights.
// Emit "new X" suggestions when the user is composing a creation query.
// Triggered by leading "new "/"create "/"+ " OR matching keywords like "campaign".
// Each suggestion deep-links into the wizard (with prefill where useful) or a
// dedicated form. Pairs with the header "+ New" menu so power users can do the
// same thing via keyboard.
function paletteNewSuggestions(q) {
  if (!q) return [];
  const isNewVerb = /^(new\s|create\s|\+\s|\+|make\s)/.test(q);
  const stripped = q.replace(/^(new|create|make|\+)\s*/, "").trim();
  // Topic keywords that should also surface "new X" even without the verb.
  const keywordHits = [
    "campaign",
    "contact",
    "company",
    "deal",
    "signal",
    "linkedin",
    "twitter",
    "email",
    "telegram",
    "vc",
    "investor",
  ];
  const matchesByKeyword = keywordHits.some((k) => q.includes(k));
  if (!isNewVerb && !matchesByKeyword) return [];

  // Phase 2 IA: launch suggestions all route through Operations Center.
  // Each entry that maps to an op deep-links to operations/op/<id> so the user
  // lands at the AccountPicker step. Pure CRM creates (Contact/Company/Deal)
  // still open their drawers directly since they're data — not operations.
  const all = [
    {
      label: "Open Operations Center",
      detail: "Browse & launch any operation",
      keys: "operations center catalog launch new",
      route: "operations",
    },
    {
      label: "Service Outreach",
      detail: "Find companies that need your services",
      keys: "service outreach campaign",
      route: "operations/op/service_outreach",
    },
    {
      label: "Hiring Outreach",
      detail: "Target companies hiring",
      keys: "hiring outreach campaign",
      route: "operations/op/hiring_outreach",
    },
    {
      label: "Competitor Targeting",
      detail: "Steal a competitor's audience",
      keys: "competitor targeting campaign",
      route: "operations/op/competitor_targeting",
    },
    {
      label: "Pursue Signals",
      detail: "AI-engage on buying signals",
      keys: "signal pursue passive",
      route: "operations/op/signal_pursue",
    },
    {
      label: "VC / Investor Outreach",
      detail: "Partner-specific pitch",
      keys: "vc investor venture capital fund",
      route: "operations/op/vc_outreach",
    },
    {
      label: "Telegram DM Campaign",
      detail: "DM Telegram contacts",
      keys: "telegram dm tg message",
      route: "operations/op/telegram_dm",
    },
    {
      label: "Email Campaign",
      detail: "One-off email send",
      keys: "email mail send campaign",
      route: "operations/op/email_campaign",
    },
    {
      label: "Email Sequence",
      detail: "Multi-step drip with delays",
      keys: "email sequence drip passive",
      route: "operations/op/email_sequence",
    },
    {
      label: "Email Warmup",
      detail: "Gradually increase send volume",
      keys: "email warmup passive",
      route: "operations/op/email_warmup",
    },
    {
      label: "Telegram Broadcast",
      detail: "Post to many groups at once",
      keys: "telegram broadcast tg post",
      route: "operations/op/tg_broadcast",
    },
    {
      label: "New Contact",
      detail: "Add to CRM",
      keys: "contact person crm",
      action: () => {
        navigate("crm");
        setTimeout(() => {
          try {
            openCrmAddContactDrawer();
          } catch {}
        }, 250);
      },
    },
    {
      label: "New Company",
      detail: "Add to CRM",
      keys: "company organization crm",
      action: () => {
        navigate("crm");
        setTimeout(() => {
          try {
            openCrmAddCompanyDrawer();
          } catch {}
        }, 250);
      },
    },
    {
      label: "New Deal",
      detail: "Track an opportunity",
      keys: "deal opportunity revenue",
      route: "deals",
    },
  ];

  const filtered = stripped
    ? all.filter((s) => s.label.toLowerCase().includes(stripped) || s.keys.includes(stripped))
    : all;
  return filtered.map((s) => ({
    kind: "wizard",
    id: s.label,
    label: s.label,
    detail: s.detail,
    route: s.route,
    action: s.action,
  }));
}

function runLocalCommandSearch(query) {
  const q = String(query || "")
    .toLowerCase()
    .trim();
  if (!q) return [];
  const results = [];

  // "new X" / "create X" / "+ X" wizard suggestions ranked above pages.
  results.push(...paletteNewSuggestions(q));

  // Pages — kept in sync with the routes table + sidebar. The palette is
  // the fastest way to jump anywhere; missing entries here mean the page
  // can't be reached without a sidebar click.
  const pages = [
    // Hub
    { route: "home", label: "Dashboard", keys: "home dashboard live map" },
    { route: "inbox", label: "Inbox", keys: "inbox replies messages notifications" },
    { route: "activity", label: "Activity", keys: "activity log events stream" },
    {
      route: "outreach-queue",
      label: "Outreach Queue",
      keys: "outreach queue sending failed retry",
    },
    { route: "people", label: "People", keys: "people discover matches recommended" },
    { route: "crm", label: "CRM", keys: "crm contacts companies pipeline" },
    { route: "deals", label: "Deals", keys: "deals pipeline revenue" },
    {
      route: "automations",
      label: "Automations",
      keys: "active workflows automations running cockpit live status presets dag",
    },
    {
      route: "workflows",
      label: "Build a workflow",
      keys: "build workflow composer canvas new edit author create",
    },
    {
      route: "proposals",
      label: "Proposals",
      keys: "proposals sales pitch deck pdf customer client offer",
    },
    {
      route: "operations",
      label: "Operations Catalog",
      keys: "operations catalog launch new ops",
    },

    // Intelligence
    {
      route: "mission",
      label: "Mission Control",
      keys: "mission control priorities top actions",
    },
    { route: "strategies", label: "Strategies", keys: "strategies automations toggle" },
    { route: "growth", label: "Growth", keys: "growth marketing platforms" },
    { route: "signals", label: "Signals", keys: "signals buying intent" },
    { route: "competitors", label: "Competitor Intel", keys: "competitors siphon audience" },
    { route: "service-match", label: "Service Match", keys: "service match icp companies fit" },
    { route: "icp", label: "ICP scoring", keys: "icp ideal customer profile scoring filters" },
    { route: "vc", label: "VC Outreach", keys: "vc venture capital fund investor" },

    // Platforms (top-level)
    { route: "x", label: "X / Twitter", keys: "x twitter dashboard" },
    { route: "telegram", label: "Telegram", keys: "telegram tg" },
    { route: "linkedin", label: "LinkedIn", keys: "linkedin li" },
    { route: "email", label: "Email", keys: "email smtp" },
    { route: "campaigns", label: "Campaigns", keys: "campaigns dm email outreach" },

    // System
    { route: "profile", label: "Your Profile", keys: "profile intro me" },
    { route: "bookmarks", label: "Bookmarks", keys: "bookmarks saved starred" },
    { route: "accounts", label: "Accounts", keys: "accounts x linkedin telegram email smtp" },
    {
      route: "health",
      label: "Health & Safety",
      keys: "health safety dnc do not contact accounts at risk",
    },
    { route: "tutorial", label: "Tutorial", keys: "tutorial guide help" },
    { route: "premium", label: "Premium", keys: "premium subscription billing addons" },
    { route: "report", label: "Report", keys: "report performance analytics" },
    { route: "settings", label: "Settings", keys: "settings config preferences ai keys" },

    // Telegram power tools (off the sidebar in v3 — only reachable here)
    {
      route: "telegram/broadcast",
      label: "Telegram Broadcast",
      keys: "telegram broadcast post group send",
    },
    {
      route: "telegram/invite",
      label: "Telegram Mass Invite",
      keys: "telegram invite group add members",
    },
    {
      route: "telegram/forward",
      label: "Telegram Forward",
      keys: "telegram forward auto repost",
    },
    {
      route: "telegram/activemember",
      label: "Telegram Active Member",
      keys: "telegram active member listener auto reply group",
    },
    {
      route: "telegram/autoreply",
      label: "Telegram Auto Reply",
      keys: "telegram autoreply auto reply dm rules",
    },
    {
      route: "telegram/passive",
      label: "Telegram Listener",
      keys: "telegram passive listener scrape",
    },
    {
      route: "telegram/scrape",
      label: "Telegram Scrape",
      keys: "telegram scrape members admins",
    },
    { route: "telegram/stories", label: "Telegram Stories", keys: "telegram stories post" },
    // Email power tools
    { route: "email/sequences", label: "Email Sequences", keys: "email sequences drip nurture" },
    { route: "email/warmup", label: "Email Warmup", keys: "email warmup deliverability ramp" },
    // Integrations + admin (orphaned from sidebar; ⌘K is the only path)
    {
      route: "integrations/luma",
      label: "Luma Subscriptions",
      keys: "luma events integrations subscription rsvp",
    },
    { route: "admin", label: "Admin Panel", keys: "admin jobs emergency stop control panel" },
    // Wizards
    {
      route: "campaigns",
      label: "New Campaign",
      keys: "new campaign wizard create",
    },
    // Boost lost its sidebar entry when Campaigns collapsed to two children, so
    // the palette is now its only discoverable way in. Same for Clients, whose
    // section is gated and therefore absent for users who have none yet.
    { route: "boost", label: "Boost", keys: "boost amplify coordinated raid project" },
    { route: "clients", label: "Clients", keys: "clients customers agency roster" },
    { route: "workflows/canvas", label: "New Workflow", keys: "new workflow automation create" },

    // Misc
    { route: "import", label: "Import", keys: "import csv contacts" },
    { route: "scan", label: "Company Scanner", keys: "scan company website" },
    { route: "group-ops", label: "Group Ops", keys: "group ops scrape announce digest" },
  ];
  for (const p of pages) {
    if (p.label.toLowerCase().includes(q) || p.keys.includes(q)) {
      results.push({
        kind: "page",
        id: p.route,
        label: p.label,
        detail: `Open ${p.route}`,
        route: p.route,
      });
    }
  }

  // Cron jobs (from current snapshot)
  const jobs = liveMapState.data?.jobs || [];
  for (const j of jobs) {
    if (j.name.toLowerCase().includes(q)) {
      const detail = j.isRunning
        ? "● running now"
        : j.lastError
          ? `${j.lastError.slice(0, 60)}`
          : `last ${fmtRel(j.lastRunAt)}`;
      results.push({
        kind: "job",
        id: j.name,
        label: j.name,
        detail,
        action: j.triggerable ? "trigger" : "view",
      });
    }
  }

  // Active campaigns
  const camps = liveMapState.data?.activeCampaigns || [];
  for (const c of camps) {
    if ((c.name || "").toLowerCase().includes(q)) {
      results.push({
        kind: "campaign",
        id: c.id,
        type: c.type,
        label: c.name,
        detail: `${c.type} · ${c.status} · ${c.sent || 0} sent`,
      });
    }
  }

  return results.slice(0, 25);
}

// Async search — local + remote (single FTS-backed /api/crm/search call,
// returns mixed contacts + companies + deals in one trip).
async function runCommandPaletteSearch(query) {
  const local = runLocalCommandSearch(query);
  const q = String(query || "").trim();
  if (q.length < 2) return local;

  const qs = `?q=${encodeURIComponent(q)}&limit=20`;
  let remote = [];
  try {
    const data = await api("crm-search", { queryString: qs }).catch(() => ({}));
    for (const r of data?.results || []) {
      if (r.type === "contact") {
        remote.push({ kind: "contact", id: r.id, label: r.label, detail: r.sublabel });
      } else if (r.type === "company") {
        remote.push({ kind: "company", id: r.id, label: r.label, detail: r.sublabel });
      } else if (r.type === "deal") {
        remote.push({ kind: "deal", id: r.id, label: r.label, detail: r.sublabel });
      }
    }
  } catch {
    /* swallow — local results still render */
  }

  // Bookmarks — small enough to fetch all and substring-match locally.
  // Cached on the state object so we don't refetch on every keystroke.
  try {
    const cp = liveMapState.commandPalette;
    if (!cp.bookmarksCache || Date.now() - (cp.bookmarksCacheAt || 0) > 60_000) {
      const data = await api("bookmarks-list", { queryString: "?limit=200" }).catch(() => ({}));
      cp.bookmarksCache = data?.bookmarks || [];
      cp.bookmarksCacheAt = Date.now();
    }
    const lc = q.toLowerCase();
    for (const b of cp.bookmarksCache) {
      if (
        (b.title || "").toLowerCase().includes(lc) ||
        (b.subtitle || "").toLowerCase().includes(lc)
      ) {
        remote.push({
          kind: "bookmark",
          id: b.itemId,
          bookmarkType: b.type,
          label: `${b.title}`,
          detail: b.subtitle || `Bookmarked ${b.type}`,
        });
        if (remote.length >= 25) break;
      }
    }
  } catch {
    /* swallow */
  }

  // Local first (pages and jobs feel snappier as direct shortcuts), then remote.
  return [...local, ...remote].slice(0, 30);
}
