const $ = (sel) => document.querySelector(sel);
const solIconUrl = chrome.runtime.getURL("icons/solana.svg");

let _buyAmounts = [0.5, 1, 2, 5];
let _sellPercents = [10, 25, 50, 100];
let _presets = [];
let _activePresetId = null;
let _activePresetValues = null;
let _allWallets = [];
let _activeWallets = [];
let _walletOrder = [];
let _latestPnl = null;

function apiCall(method, path, body) {
  return new Promise((resolve) => {
    chrome.runtime.sendMessage({ type: "API_CALL", method, path, body }, (resp) => {
      if (chrome.runtime.lastError) { resolve({ error: "Disconnected" }); return; }
      resolve(resp);
    });
  });
}

function setStatus(text, type) {
  const el = $("#fp-status");
  el.textContent = text;
  el.className = "fp-status" + (type ? ` fp-${type}` : "");
  if (type) setTimeout(() => { el.textContent = ""; el.className = "fp-status"; }, 1500);
}

function postToast(message, type = "success", durationMs = null) {
  try {
    window.parent.postMessage({ type: "slotshark-toast", message, toastType: type, durationMs }, "*");
  } catch {}
}

function isNoTokensError(msg) {
  return typeof msg === "string" && (msg.includes("No token balance") || msg.includes("No tokens to sell"));
}

function renderButtons() {
  const buyGrid = $("#fp-buy-grid");
  const sellGrid = $("#fp-sell-grid");

  buyGrid.innerHTML = _buyAmounts.map((a) =>
    `<button class="fp-trade-btn" data-action="buy" data-amount="${a}"><img class="fp-sol-icon" src="${solIconUrl}" alt="">${a}</button>`
  ).join("");

  sellGrid.innerHTML = _sellPercents.map((p) =>
    `<button class="fp-trade-btn fp-sell" data-action="sell" data-amount="${p}">${p}%</button>`
  ).join("");

  buyGrid.querySelectorAll(".fp-trade-btn").forEach((btn) => btn.addEventListener("click", handleTrade));
  sellGrid.querySelectorAll(".fp-trade-btn").forEach((btn) => btn.addEventListener("click", handleTrade));
  requestAnimationFrame(() => { reportHeight(); setTimeout(reportHeight, 50); });
}

function getMint() {
  return window._floatingPanelMint || "";
}

function flashButton(btn) {
  btn.classList.remove("fp-click-flash");
  void btn.offsetWidth; // restart CSS animation on rapid re-clicks
  btn.classList.add("fp-click-flash");
}

async function handleTrade(e) {
  const btn = e.currentTarget;
  const action = btn.dataset.action;
  const amount = parseFloat(btn.dataset.amount);

  flashButton(btn);

  window.parent.postMessage({ type: "slotshark-get-mint" }, "*");
  await new Promise((r) => setTimeout(r, 50));
  const mint = getMint();

  if (!mint) { setStatus("Navigate to a token page first", "error"); return; }

  const walletData = await chrome.storage.local.get(["slotshark.activeWallets"]);
  const wallets = walletData["slotshark.activeWallets"] || [];

  if (wallets.length === 0) {
    setStatus("No active wallets", "error");
    return;
  }

  const v = _activePresetValues || {};
  const prefix = action === "buy" ? "buy" : "sell";
  const calls = wallets.map((wallet) => {
    const body = {
      mint,
      wallet,
      slippage: v[`${prefix}-slippage`] ?? 20,
      antimev: v[`${prefix}-anti-mev`] ?? true,
    };
    if (action === "buy") { body.solAmount = amount; body.skipIfBought = false; }
    else { body.sellPercent = amount; }
    if (v[`${prefix}-tip`] != null) body.tip = v[`${prefix}-tip`];
    if (v[`${prefix}-fee`] != null) body.priorityFee = v[`${prefix}-fee`];
    return { method: "POST", path: `/${action}`, body };
  });

  let results;
  if (calls.length === 1) {
    const resp = await apiCall(calls[0].method, calls[0].path, calls[0].body);
    results = [resp];
  } else {
    const resp = await new Promise((resolve) => {
      chrome.runtime.sendMessage({ type: "API_BATCH", calls }, (r) => {
        if (chrome.runtime.lastError) { resolve({ results: [] }); return; }
        resolve(r);
      });
    });
    results = resp.results || [];
  }

  const actionLabel = action === "buy" ? "Buy" : "Sell";
  const ok = results.filter((r) => r?.data?.success).length;
  let failed = results.filter((r) => !r?.data?.success);

  // Hide "no tokens owned" failures when at least one wallet succeeded.
  if (ok > 0 && failed.length > 0) {
    failed = failed.filter((r) => !isNoTokensError(r?.data?.error || r?.error || ""));
  }

  if (ok > 0) {
    postToast(`${actionLabel} sent from ${ok} wallet${ok > 1 ? "s" : ""}`, "success");
  }
  if (failed.length > 0) {
    const reason = failed[0]?.data?.error || failed[0]?.error || "unknown";
    postToast(`${failed.length} ${actionLabel.toLowerCase()}${failed.length > 1 ? "s" : ""} failed: ${reason}`, "error");
    setStatus(reason, "error");
  }
  setTimeout(() => fetchPnl(mint), 1000);

  const signatures = results
    .filter((r) => r?.data?.success && r?.data?.signature)
    .map((r) => r.data.signature);
  if (signatures.length > 0) pollTxOutcomes(signatures, actionLabel);
}

async function pollTxOutcomes(signatures, actionLabel) {
  const outcomes = await Promise.all(signatures.map(pollOneSig));
  const confirmed = outcomes.filter((o) => o.status === "success").length;
  const failed = outcomes.filter((o) => o.status === "failed");
  const pending = outcomes.filter((o) => o.status === "pending").length;
  const total = signatures.length;
  const single = total === 1;

  if (failed.length > 0) {
    const first = failed[0];
    const reason = first.reason || (first.errorCode != null ? `error ${first.errorCode}` : "reverted");
    const msg = single
      ? `${actionLabel} failed: ${reason}`
      : `${actionLabel}: ${failed.length}/${total} failed on-chain (${reason})`;
    postToast(msg, "error");
  } else if (confirmed === total) {
    const msg = single ? `${actionLabel} confirmed on-chain` : `${actionLabel}: ${confirmed}/${total} confirmed on-chain`;
    postToast(msg, "success");
  } else if (pending > 0) {
    const msg = single
      ? `${actionLabel} not yet confirmed — check Solscan`
      : `${actionLabel}: ${pending}/${total} not yet confirmed — check Solscan`;
    postToast(msg, "error");
  }
}

async function pollOneSig(sig) {
  for (let i = 0; i < 30; i++) {
    try {
      const resp = await apiCall("GET", `/tx-status/${sig}`);
      const status = resp?.data?.status;
      if (status === "success" || status === "failed") return resp.data;
    } catch { /* transient — keep polling */ }
    await new Promise((r) => setTimeout(r, 500));
  }
  return { status: "pending" };
}

function updatePresetDisplay() {
  const active = _presets.find((p) => p.id === _activePresetId) || _presets[0];
  if (active) {
    $("#fp-preset-emoji").textContent = active.emoji;
    $("#fp-preset-label").textContent = active.label;
    if (active.values) {
      _activePresetValues = active.values;
      _buyAmounts = active.values["buy-amounts"] || _buyAmounts;
      _sellPercents = active.values["sell-percents"] || _sellPercents;
    }
  } else {
    _activePresetValues = null;
    $("#fp-preset-emoji").textContent = "";
    $("#fp-preset-label").textContent = "No preset";
  }
  renderButtons();
}

function renderPresetDropdown() {
  const dd = $("#fp-preset-dropdown");
  if (_presets.length === 0) {
    dd.innerHTML = '<div style="padding:10px;text-align:center;color:#555;font-size:11px;">No presets</div>';
    return;
  }
  dd.innerHTML = _presets.map((p) => {
    const isActive = p.id === _activePresetId || (!_activePresetId && p === _presets[0]);
    return `<button class="fp-dropdown-item${isActive ? " active" : ""}" data-id="${p.id}">
      <span class="fp-dd-emoji">${p.emoji}</span>
      <span class="fp-dd-label">${p.label}</span>
      ${isActive ? '<span class="fp-dd-check">&#10003;</span>' : ""}
    </button>`;
  }).join("");

  dd.querySelectorAll(".fp-dropdown-item").forEach((btn) => {
    btn.addEventListener("click", async () => {
      _activePresetId = btn.dataset.id;
      await chrome.storage.local.set({ "slotshark.activePreset": _activePresetId });
      updatePresetDisplay();
      dd.style.display = "none";
    });
  });
}

$("#fp-preset-btn").addEventListener("click", (e) => {
  e.stopPropagation();
  const dd = $("#fp-preset-dropdown");
  if (dd.style.display === "none") {
    renderPresetDropdown();
    dd.style.display = "block";
  } else {
    dd.style.display = "none";
  }
});

function updateWalletDisplay() {
  $("#fp-wallet-badge").textContent = _activeWallets.length;
}

$("#fp-wallet-toggle").addEventListener("click", () => {
  window.parent.postMessage({ type: "slotshark-toggle-wallets" }, "*");
});

window.addEventListener("message", (e) => {
  if (e.data?.type === "slotshark-wallet-panel-state") {
    const btn = $("#fp-wallet-toggle");
    if (e.data.open) {
      btn.style.color = "#1a8cff";
    } else {
      btn.style.color = "";
    }
  }
});

document.addEventListener("click", (e) => {
  const pdd = $("#fp-preset-dropdown");
  if (pdd && !pdd.contains(e.target) && !e.target.closest("#fp-preset-btn")) pdd.style.display = "none";
});

async function loadPresets() {
  const data = await chrome.storage.local.get(["slotshark.presets", "slotshark.activePreset"]);
  _presets = data["slotshark.presets"] || [];
  _activePresetId = data["slotshark.activePreset"] || null;
  updatePresetDisplay();
}

async function loadWallets() {
  const data = await chrome.storage.local.get(["slotshark.activeWallets", "slotshark.walletOrder"]);
  _activeWallets = data["slotshark.activeWallets"] || [];
  _walletOrder = data["slotshark.walletOrder"] || [];
  updateWalletDisplay();

  const resp = await apiCall("GET", "/auth/me");
  if (resp?.data?.success) {
    _allWallets = (resp.data.wallets || []).filter((w) => w.active);
    // Drop cached wallets the current region's server doesn't know (post-region-switch).
    const serverPks = new Set(_allWallets.map((w) => w.pubkey));
    const filtered = _activeWallets.filter((pk) => serverPks.has(pk));
    if (filtered.length !== _activeWallets.length) {
      _activeWallets = filtered;
      try { await chrome.storage.local.set({ "slotshark.activeWallets": _activeWallets }); } catch {}
    }
  }
  updateWalletDisplay();
}

$("#fp-settings").addEventListener("click", () => {
  chrome.runtime.sendMessage({ type: "OPEN_SETTINGS", hash: "wallets" });
});

async function handleSellInitials(e) {
  const btn = e.currentTarget;
  flashButton(btn);

  const mint = getMint();
  if (!mint) { setStatus("Navigate to a token page first", "error"); return; }

  if (!_latestPnl) { setStatus("PnL not loaded yet", "error"); return; }
  if (!_latestPnl.solSpent || _latestPnl.solSpent <= 0) {
    setStatus("No buy history for this token", "error"); return;
  }
  const remaining = _latestPnl.solSpent - _latestPnl.solReceived;
  if (remaining <= 0) { setStatus("Already at breakeven or profit", "error"); return; }
  if (!_latestPnl.holdingValueSol || _latestPnl.holdingValueSol <= 0) {
    setStatus("No holding value", "error"); return;
  }
  const pct = Math.min(100, Math.round((remaining / _latestPnl.holdingValueSol) * 100));
  if (pct <= 0) { setStatus("Nothing to sell", "error"); return; }

  const walletData = await chrome.storage.local.get(["slotshark.activeWallets"]);
  const wallets = walletData["slotshark.activeWallets"] || [];
  if (wallets.length === 0) {
    setStatus("No active wallets", "error");
    return;
  }

  const v = _activePresetValues || {};
  const calls = wallets.map((wallet) => {
    const body = {
      mint,
      wallet,
      sellPercent: pct,
      slippage: v["sell-slippage"] ?? 20,
      antimev: v["sell-anti-mev"] ?? true,
    };
    if (v["sell-tip"] != null) body.tip = v["sell-tip"];
    if (v["sell-fee"] != null) body.priorityFee = v["sell-fee"];
    return { method: "POST", path: "/sell", body };
  });

  let results;
  if (calls.length === 1) {
    const resp = await apiCall(calls[0].method, calls[0].path, calls[0].body);
    results = [resp];
  } else {
    const resp = await new Promise((resolve) => {
      chrome.runtime.sendMessage({ type: "API_BATCH", calls }, (r) => {
        if (chrome.runtime.lastError) { resolve({ results: [] }); return; }
        resolve(r);
      });
    });
    results = resp.results || [];
  }

  const ok = results.filter((r) => r?.data?.success).length;
  let failed = results.filter((r) => !r?.data?.success);

  if (ok > 0 && failed.length > 0) {
    failed = failed.filter((r) => !isNoTokensError(r?.data?.error || r?.error || ""));
  }

  if (ok > 0) {
    postToast(`Sell sent from ${ok} wallet${ok > 1 ? "s" : ""}`, "success");
  }
  if (failed.length > 0) {
    const reason = failed[0]?.data?.error || failed[0]?.error || "unknown";
    postToast(`${failed.length} sell${failed.length > 1 ? "s" : ""} failed: ${reason}`, "error");
    setStatus(reason, "error");
  }
  setTimeout(() => fetchPnl(mint), 1000);
}

$("#fp-sell-initials").addEventListener("click", handleSellInitials);

$("#fp-minimize").addEventListener("click", () => {
  window.parent.postMessage({ type: "slotshark-minimize" }, "*");
});

$("#fp-drag-handle").addEventListener("mousedown", (e) => {
  if (e.target.closest(".fp-btn-icon") || e.target.closest(".fp-header-preset")) return;
  e.preventDefault();
  window.parent.postMessage({ type: "slotshark-drag-start", clientX: e.clientX, clientY: e.clientY }, "*");
  const onUp = () => {
    window.parent.postMessage({ type: "slotshark-drag-end" }, "*");
    window.removeEventListener("mouseup", onUp);
  };
  window.addEventListener("mouseup", onUp);
});


function initPnlIcons() {
  const url = chrome.runtime.getURL("icons/solana.svg");
  ["fp-pnl-icon-1", "fp-pnl-icon-2", "fp-pnl-icon-3", "fp-pnl-icon-4"].forEach((id) => {
    const el = document.getElementById(id);
    if (el) el.src = url;
  });
}
initPnlIcons();

async function fetchPnl(mint) {
  if (!mint) return;
  try {
    const walletData = await chrome.storage.local.get(["slotshark.activeWallets"]);
    var activeWallets = (walletData["slotshark.activeWallets"] || []).join(",");
  } catch { stopPnlPolling(); return; }
  const resp = await apiCall("GET", `/token/${mint}/pnl?wallets=${encodeURIComponent(activeWallets)}`);
  if (!resp?.data?.success) {
    return;
  }
  const d = resp.data;
  _latestPnl = d;
  const fmt = (v) => {
    if (v === 0) return "0";
    return parseFloat(v.toFixed(3)).toString();
  };
  $("#fp-pnl-spent").textContent = fmt(d.solSpent);
  $("#fp-pnl-received").textContent = fmt(d.solReceived);
  $("#fp-pnl-holding").textContent = fmt(d.holdingValueSol);
  const sign = d.pnlSol >= 0 ? "+" : "";
  // pnlPercent is null for orphan sells (bought externally, sold via us) — infinite %.
  const pctStr = typeof d.pnlPercent === "number" ? `${sign}${d.pnlPercent}%` : "+∞%";
  $("#fp-pnl-value").textContent = `${sign}${fmt(d.pnlSol)}(${pctStr})`;
  const resultEl = $("#fp-pnl-result");
  resultEl.classList.remove("fp-pnl-positive", "fp-pnl-negative");
  resultEl.classList.add(d.pnlSol >= 0 ? "fp-pnl-positive" : "fp-pnl-negative");
  reportHeight();
}

let _pnlInterval = null;

function startPnlPolling() {
  stopPnlPolling();
  _pnlInterval = setInterval(() => {
    const mint = getMint();
    if (mint) fetchPnl(mint);
  }, 1000);
}

function stopPnlPolling() {
  if (_pnlInterval) { clearInterval(_pnlInterval); _pnlInterval = null; }
}

window.addEventListener("message", (e) => {
  if (e.data?.type === "slotshark-pause") {
    stopPnlPolling();
    return;
  }
  if (e.data?.type === "slotshark-resume") {
    const mint = getMint();
    if (mint) startPnlPolling();
    return;
  }
  if (e.data?.type === "slotshark-set-mint") {
    const newMint = e.data.mint;
    if (newMint && newMint !== window._floatingPanelMint) {
      window._floatingPanelMint = newMint;
      fetchPnl(newMint);
      startPnlPolling();
    } else if (newMint && !_pnlInterval) {
      window._floatingPanelMint = newMint;
      fetchPnl(newMint);
      startPnlPolling();
    }
  }
});

chrome.storage.onChanged.addListener((changes) => {
  if (changes["slotshark.presets"] || changes["slotshark.activePreset"]) loadPresets();
  if (changes["slotshark.activeWallets"]) {
    _activeWallets = changes["slotshark.activeWallets"].newValue || [];
    updateWalletDisplay();
  }
});

function reportHeight() {
  const wrapper = document.getElementById("fp-wrapper");
  const h = wrapper ? wrapper.offsetHeight : document.documentElement.scrollHeight;
  window.parent.postMessage({ type: "slotshark-content-height", height: h }, "*");
}

loadPresets();
loadWallets();

window.parent.postMessage({ type: "slotshark-get-mint" }, "*");
setTimeout(() => window.parent.postMessage({ type: "slotshark-get-mint" }, "*"), 500);
setTimeout(() => window.parent.postMessage({ type: "slotshark-get-mint" }, "*"), 1500);

new MutationObserver(() => setTimeout(reportHeight, 5)).observe(document.body, { childList: true, subtree: true });
setTimeout(reportHeight, 50);
setTimeout(reportHeight, 200);
setTimeout(reportHeight, 500);
