/**
 * background.js — Kling Token Bot Service Worker
 * Intercepts Authorization headers from api.weavy.ai requests
 * and auto-captures Bearer tokens.
 */

const SERVER_URLS = ["http://localhost:3001", "http://localhost:3000"];
const TOKEN_STORAGE_KEY = "kling_captured_tokens";
const MAX_TOKENS = 50;

// ─── Token capture via webRequest ──────────────────────────────────────────

chrome.webRequest.onSendHeaders.addListener(
  (details) => {
    const authHeader = details.requestHeaders?.find(
      (h) => h.name.toLowerCase() === "authorization"
    );
    if (!authHeader?.value?.startsWith("Bearer ")) return;

    const token = authHeader.value.replace("Bearer ", "").trim();

    // Validate it's a JWT (3 parts)
    const parts = token.split(".");
    if (parts.length !== 3) return;

    // Decode payload to check it's a valid Weavy/Firebase token
    try {
      const payload = JSON.parse(atob(parts[1]));
      if (!payload.exp || !payload.sub) return;

      // Already expired? skip
      if (Date.now() / 1000 > payload.exp) return;

      saveToken({
        token,
        email: payload.email || payload.name || "unknown",
        exp: payload.exp,
        sub: payload.sub,
        capturedAt: Date.now(),
        source: details.url,
        sent: false,
      });
    } catch {}
  },
  {
    urls: ["https://api.weavy.ai/*", "https://*.figma.com/*"],
    types: ["xmlhttprequest", "ping", "other"],
  },
  ["requestHeaders", "extraHeaders"]
);

// ─── Token storage ──────────────────────────────────────────────────────────

async function getTokens() {
  const result = await chrome.storage.local.get(TOKEN_STORAGE_KEY);
  return result[TOKEN_STORAGE_KEY] || [];
}

async function saveToken(tokenData) {
  const tokens = await getTokens();

  // Skip if same token already exists
  if (tokens.some((t) => t.token === tokenData.token)) return;

  // Skip if same user (sub) already has a valid token
  const existingSub = tokens.find(
    (t) => t.sub === tokenData.sub && !isExpired(t)
  );
  if (existingSub) {
    // Replace if new one has later expiry
    if (tokenData.exp > existingSub.exp) {
      const idx = tokens.indexOf(existingSub);
      tokens[idx] = tokenData;
    } else return;
  } else {
    tokens.unshift(tokenData);
  }

  // Keep max tokens
  const trimmed = tokens.slice(0, MAX_TOKENS);
  await chrome.storage.local.set({ [TOKEN_STORAGE_KEY]: trimmed });

  // Notify
  notifyNewToken(tokenData);

  // Auto-send if enabled
  const { autoSend, serverUrl } = await chrome.storage.local.get(["autoSend", "serverUrl"]);
  if (autoSend) {
    await sendTokenToServer(tokenData, serverUrl || SERVER_URLS[0]);
  }

  // Update badge
  updateBadge();
}

function isExpired(tokenData) {
  return Date.now() / 1000 > tokenData.exp;
}

function isJwtExpired(token) {
  try {
    if (!token) return true;
    const parts = token.split(".");
    if (parts.length !== 3) return true;
    const payload = JSON.parse(atob(parts[1]));
    return Date.now() / 1000 > payload.exp;
  } catch {
    return true;
  }
}

// ─── Send token to server ───────────────────────────────────────────────────

async function sendTokenToServer(tokenData, serverUrl, password = "") {
  const url = (serverUrl || SERVER_URLS[0]).replace(/\/$/, "");
  const adminPass = password || (await getAdminPass());

  try {
    const res = await fetch(`${url}/api/admin/accounts`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-admin-password": adminPass,
      },
      body: JSON.stringify({
        token: tokenData.token,
        label: tokenData.email || "Auto-Captured",
        recipeId: "",
      }),
    });

    const data = await res.json();
    if (res.ok) {
      // Mark as sent
      await markTokenSent(tokenData.token, true);
      return { ok: true, accountId: data.account?.id };
    } else {
      return { ok: false, error: data.error };
    }
  } catch (err) {
    return { ok: false, error: err.message };
  }
}

async function markTokenSent(token, sent) {
  const tokens = await getTokens();
  const t = tokens.find((t) => t.token === token);
  if (t) {
    t.sent = sent;
    await chrome.storage.local.set({ [TOKEN_STORAGE_KEY]: tokens });
  }
}

async function getAdminPass() {
  const { adminPassword } = await chrome.storage.local.get("adminPassword");
  return adminPassword || "Tekno@Project03";
}

// ─── Alarm: auto-cleanup expired tokens ───────────────────────────────────

chrome.alarms.create("cleanup-expired", { periodInMinutes: 10 });

chrome.alarms.onAlarm.addListener(async (alarm) => {
  if (alarm.name === "cleanup-expired") {
    const tokens = await getTokens();
    const active = tokens.filter((t) => !isExpired(t));
    if (active.length !== tokens.length) {
      await chrome.storage.local.set({ [TOKEN_STORAGE_KEY]: active });
      updateBadge();
    }
  }
});

// ─── Badge & notifications ──────────────────────────────────────────────────

async function updateBadge() {
  const tokens = await getTokens();
  const active = tokens.filter((t) => !isExpired(t));
  const unsent = active.filter((t) => !t.sent).length;

  if (unsent > 0) {
    chrome.action.setBadgeText({ text: String(unsent) });
    chrome.action.setBadgeBackgroundColor({ color: "#6366f1" });
  } else if (active.length > 0) {
    chrome.action.setBadgeText({ text: "✓" });
    chrome.action.setBadgeBackgroundColor({ color: "#3fb950" });
  } else {
    chrome.action.setBadgeText({ text: "" });
  }
}

function notifyNewToken(tokenData) {
  chrome.notifications.create({
    type: "basic",
    iconUrl: "icons/icon48.png",
    title: "🎯 Token Baru Ditangkap!",
    message: `Email: ${tokenData.email}\nExpiry: ${formatExp(tokenData.exp)}`,
    priority: 1,
  });
}

function formatExp(exp) {
  const diff = exp - Date.now() / 1000;
  if (diff <= 0) return "Expired";
  const m = Math.floor(diff / 60);
  return `${m} menit lagi`;
}

// ─── Wizard orchestration ──────────────────────────────────────────────────

const WIZARD_STORAGE_KEY = "wizard_state";
const FIGMA_PASSWORD_DEFAULT = "Tekno@Kling2026";

async function getWizardState() {
  const r = await chrome.storage.local.get(WIZARD_STORAGE_KEY);
  return r[WIZARD_STORAGE_KEY] || {
    running: false,
    step: "IDLE",
    log: [],
    email: null,
    password: null,
    tabId: null,
    totalRequested: 0,
    completed: 0,
    failed: 0,
    currentRound: 0,
    reviveEnabled: true,
  };
}

async function setWizardState(state) {
  await chrome.storage.local.set({ [WIZARD_STORAGE_KEY]: state });
}

function wizardLog(state, msg) {
  const ts = new Date().toLocaleTimeString("id-ID");
  state.log.unshift(`[${ts}] ${msg}`);
  if (state.log.length > 50) state.log = state.log.slice(0, 50);
}

async function sendToTab(tabId, message, timeoutMs = 30000) {
  return new Promise((resolve) => {
    const timer = setTimeout(() => resolve({ ok: false, error: "Timeout" }), timeoutMs);
    chrome.tabs.sendMessage(tabId, message, (response) => {
      clearTimeout(timer);
      if (chrome.runtime.lastError) {
        resolve({ ok: false, error: chrome.runtime.lastError.message });
      } else {
        resolve(response || { ok: false, error: "No response" });
      }
    });
  });
}

async function waitForTabLoad(tabId, timeoutMs = 30000) {
  return new Promise((resolve) => {
    const timer = setTimeout(() => {
      chrome.tabs.onUpdated.removeListener(listener);
      resolve(false);
    }, timeoutMs);

    function listener(id, info) {
      if (id === tabId && info.status === "complete") {
        clearTimeout(timer);
        chrome.tabs.onUpdated.removeListener(listener);
        resolve(true);
      }
    }
    chrome.tabs.onUpdated.addListener(listener);
  });
}

async function openTabAndWait(url, timeoutMs = 30000) {
  const tab = await chrome.tabs.create({ url, active: true });
  await waitForTabLoad(tab.id, timeoutMs);
  // Extra wait for content scripts to initialize
  await new Promise(r => setTimeout(r, 2000));
  return tab;
}

async function closeTabSafely(tabId) {
  try {
    await chrome.tabs.remove(tabId);
  } catch (e) { /* tab already closed */ }
}

// ── Wizard Runner ──────────────────────────────────────────────────────────

async function runWizardRound(state) {
  const round = state.currentRound + 1;
  state.currentRound = round;
  state.step = "STARTING";
  wizardLog(state, `🔄 Ronde ${round}/${state.totalRequested} dimulai...`);
  await setWizardState(state);

  let icloudTabId = null;
  let figmaTabId = null;
  let weavyTabId = null;

  try {
    // ── Check if there is an expired/dead account to revive first ──────────
    let reviveAccount = null;
    if (state.reviveEnabled) {
      try {
        const { serverUrl, adminPassword } = await chrome.storage.local.get(["serverUrl", "adminPassword"]);
        const url = (serverUrl || SERVER_URLS[0]).replace(/\/$/, "");
        const adminPass = adminPassword || "Tekno@Project03";
        
        wizardLog(state, "🔍 Mengecek akun mati di pool server...");
        await setWizardState(state);

        const res = await fetch(`${url}/api/admin/accounts/raw`, {
          headers: { "x-admin-password": adminPass }
        });
        if (res.ok) {
          const data = await res.json();
          const accounts = data.accounts || [];
          
          const dead = accounts.filter(a => {
            const jwtExpired = isJwtExpired(a.token);
            const expired = jwtExpired || Boolean(a.expired) || (a.runs || 0) >= 3;
            return expired && (a.runs || 0) < 3 && a.email && a.password;
          });
          
          if (dead.length > 0) {
            reviveAccount = dead[0];
            wizardLog(state, `🔄 Menemukan akun mati: ${reviveAccount.email} (Limit: ${reviveAccount.runs || 0}/3). Mencoba revive...`);
            await setWizardState(state);
          } else {
            wizardLog(state, "ℹ️ Tidak ada akun mati yang bisa di-revive.");
            await setWizardState(state);
          }
        } else {
          wizardLog(state, `⚠️ Gagal mengambil list akun: HTTP ${res.status}`);
          await setWizardState(state);
        }
      } catch (e) {
        wizardLog(state, `⚠️ Gagal cek akun mati: ${e.message}`);
        await setWizardState(state);
      }
    }

    if (reviveAccount) {
      state.step = "CAPTURING_TOKEN";
      state.email = reviveAccount.email;
      state.password = reviveAccount.password;
      wizardLog(state, `🔑 Menggunakan password figma: ${state.password.substring(0, 3)}***`);
      await setWizardState(state);

      wizardLog(state, "8️⃣ Membuka weave.figma.com untuk revive...");
      const weavyTab = await openTabAndWait("https://weave.figma.com/", 30000);
      weavyTabId = weavyTab.id;
      await new Promise(r => setTimeout(r, 3000));

      await sendToTab(weavyTabId, { action: "WIZARD_WEAVY_LOGIN" }, 15000);

      let captured = false;
      for (let i = 0; i < 15; i++) {
        await new Promise(r => setTimeout(r, 4000));
        
        const tokens = await getTokens();
        const found = tokens.find(t => t.email === state.email && !isExpired(t));
        if (found) {
          captured = true;
          wizardLog(state, `🎯 Token revive berhasil ditangkap untuk ${state.email}!`);
          
          const { serverUrl } = await chrome.storage.local.get("serverUrl");
          const sendResult = await sendTokenToServer(found, serverUrl || SERVER_URLS[0]);
          if (sendResult.ok) {
            wizardLog(state, "📤 Token revive berhasil dikirim ke server!");
          } else {
            wizardLog(state, `⚠️ Kirim ke server gagal: ${sendResult.error}`);
          }
          await setWizardState(state);
          break;
        }

        // Auto-fill login if Figma tab opens (for credentials entry)
        try {
          const tabs = await chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT });
          const figmaTab = tabs.find(t => t.url && t.url.includes("figma.com") && (t.url.includes("login") || t.url.includes("signin")));
          if (figmaTab) {
            figmaTabId = figmaTab.id;
            await sendToTab(figmaTabId, {
              action: "WIZARD_FILL_SIGNUP",
              email: state.email,
              password: state.password
            }, 5000).catch(() => {});
          }
        } catch (e) {}
      }

      if (!captured) {
        throw new Error("Gagal revive: Token tidak ditangkap dalam 1 menit. Pastikan sudah login figma.");
      }

      await closeTabSafely(weavyTabId);
      weavyTabId = null;

      state.completed++;
      state.step = "ROUND_DONE";
      wizardLog(state, `✅ Revive ronde ${round} selesai!`);
      await setWizardState(state);
      return;
    }
    // ── STEP 1: Open iCloud Setan & Login ──────────────────────────────────
    state.step = "ICLOUD_LOGIN";
    wizardLog(state, "1️⃣ Membuka iCloud Setan...");
    await setWizardState(state);

    const { icloudSetanVoucher } = await chrome.storage.local.get("icloudSetanVoucher");
    if (!icloudSetanVoucher) {
      throw new Error("Voucher iCloud Setan belum diatur! Buka tab Setelan.");
    }

    const icloudTab = await openTabAndWait("https://icloud-setan.texaproject.com/");
    icloudTabId = icloudTab.id;
    await new Promise(r => setTimeout(r, 2000));

    const loginResult = await sendToTab(icloudTabId, {
      action: "WIZARD_LOGIN",
      voucher: icloudSetanVoucher
    }, 30000);

    if (!loginResult.ok) {
      throw new Error(`Login iCloud Setan gagal: ${loginResult.error}`);
    }
    wizardLog(state, "✅ Login iCloud Setan berhasil");
    await setWizardState(state);

    // ── STEP 2: Generate Email ─────────────────────────────────────────────
    state.step = "GENERATING_EMAIL";
    wizardLog(state, "2️⃣ Generate email iCloud...");
    await setWizardState(state);
    await new Promise(r => setTimeout(r, 2000));

    const genResult = await sendToTab(icloudTabId, {
      action: "WIZARD_GENERATE_EMAIL",
    }, 30000);

    if (!genResult.ok) {
      if (genResult.error === "LIMIT") {
        throw new Error("Limit tercapai! Coba server lain atau tunggu.");
      }
      throw new Error(`Generate email gagal: ${genResult.error}`);
    }

    state.email = genResult.email;
    const suffix = Math.random().toString(36).slice(2, 6);
    state.password = `${FIGMA_PASSWORD_DEFAULT}${suffix}!`;
    wizardLog(state, `✅ Email: ${state.email}`);
    await setWizardState(state);

    // ── STEP 3: Reserve Email ──────────────────────────────────────────────
    state.step = "RESERVING_EMAIL";
    wizardLog(state, "3️⃣ Reserve email...");
    await setWizardState(state);

    const reserveResult = await sendToTab(icloudTabId, {
      action: "WIZARD_RESERVE_EMAIL",
    }, 40000);

    if (!reserveResult.ok) {
      throw new Error(`Reserve email gagal: ${reserveResult.error}`);
    }
    wizardLog(state, "✅ Email berhasil direserve");
    await setWizardState(state);

    // ── STEP 4: Open Figma Signup ──────────────────────────────────────────
    state.step = "FIGMA_SIGNUP";
    wizardLog(state, "4️⃣ Membuka Figma signup...");
    await setWizardState(state);

    const figmaTab = await openTabAndWait("https://www.figma.com/signup");
    figmaTabId = figmaTab.id;
    await new Promise(r => setTimeout(r, 3000));

    const signupResult = await sendToTab(figmaTabId, {
      action: "WIZARD_FILL_SIGNUP",
      email: state.email,
      password: state.password,
    }, 30000);

    wizardLog(state, "✅ Form signup diisi. Cek CAPTCHA jika ada...");
    await setWizardState(state);

    // ── STEP 5: Wait for signup success / CAPTCHA ──────────────────────────
    state.step = "WAITING_CAPTCHA";
    wizardLog(state, "5️⃣ Menunggu signup selesai (CAPTCHA manual jika ada)...");
    await setWizardState(state);

    // Poll for up to 3 minutes for signup to complete
    let signupDone = false;
    for (let i = 0; i < 36; i++) {
      await new Promise(r => setTimeout(r, 5000));
      try {
        const check = await sendToTab(figmaTabId, { action: "WIZARD_CHECK_SIGNUP" }, 5000);
        if (check.ok && check.isSuccess) {
          signupDone = true;
          wizardLog(state, "✅ Signup berhasil!");
          await setWizardState(state);
          break;
        }
        if (check.ok && check.hasCaptcha) {
          state.step = "WAITING_CAPTCHA";
          if (i % 6 === 0) wizardLog(state, "⏳ Menunggu CAPTCHA diselesaikan...");
          await setWizardState(state);
        }
        if (check.ok && check.errorText) {
          wizardLog(state, `⚠️ Figma: ${check.errorText}`);
          await setWizardState(state);
        }
      } catch (e) {
        // Tab might have navigated, check URL
        try {
          const tab = await chrome.tabs.get(figmaTabId);
          if (tab.url && (tab.url.includes("files") || tab.url.includes("recents") || tab.url.includes("verify"))) {
            signupDone = true;
            wizardLog(state, "✅ Signup berhasil (URL redirect detected)");
            await setWizardState(state);
            break;
          }
        } catch (e2) { break; }
      }
    }

    if (!signupDone) {
      // Check one more time via URL
      try {
        const tab = await chrome.tabs.get(figmaTabId);
        if (tab.url && !tab.url.includes("signup")) {
          signupDone = true;
        }
      } catch (e) {}
    }

    if (!signupDone) {
      throw new Error("Timeout menunggu signup. CAPTCHA tidak diselesaikan?");
    }

    // Close Figma tab
    await closeTabSafely(figmaTabId);
    figmaTabId = null;

    // ── STEP 6: Wait for Verification Email ────────────────────────────────
    state.step = "WAITING_VERIFY";
    wizardLog(state, "6️⃣ Menunggu email verifikasi dari Figma...");
    await setWizardState(state);

    // Focus back on iCloud tab
    try { await chrome.tabs.update(icloudTabId, { active: true }); } catch (e) {}
    await new Promise(r => setTimeout(r, 3000));

    let verifyLink = null;
    for (let attempt = 0; attempt < 24; attempt++) { // 2 minutes max
      await new Promise(r => setTimeout(r, 5000));
      
      const inboxResult = await sendToTab(icloudTabId, {
        action: "WIZARD_CHECK_INBOX",
      }, 15000);

      if (inboxResult.ok && inboxResult.hasFigma) {
        wizardLog(state, "📬 Email Figma ditemukan! Mengambil link verifikasi...");
        await setWizardState(state);

        await new Promise(r => setTimeout(r, 2000));
        const linkResult = await sendToTab(icloudTabId, {
          action: "WIZARD_GET_VERIFY_LINK",
        }, 20000);

        if (linkResult.ok && linkResult.url) {
          verifyLink = linkResult.url;
          break;
        } else {
          wizardLog(state, `⚠️ Link tidak ditemukan, coba lagi...`);
          await setWizardState(state);
        }
      } else {
        if (attempt % 4 === 0) {
          wizardLog(state, `⏳ Menunggu email... (${attempt * 5}s)`);
          await setWizardState(state);
        }
      }
    }

    if (!verifyLink) {
      throw new Error("Timeout menunggu email verifikasi dari Figma");
    }

    // Close iCloud tab
    await closeTabSafely(icloudTabId);
    icloudTabId = null;

    // ── STEP 7: Open Verification Link ─────────────────────────────────────
    state.step = "VERIFYING";
    wizardLog(state, `7️⃣ Membuka link verifikasi...`);
    await setWizardState(state);

    const verifyTab = await openTabAndWait(verifyLink, 30000);
    await new Promise(r => setTimeout(r, 5000));
    await closeTabSafely(verifyTab.id);

    wizardLog(state, "✅ Email terverifikasi!");
    await setWizardState(state);

    // ── STEP 8: Open Weavy & Capture Token ─────────────────────────────────
    state.step = "CAPTURING_TOKEN";
    wizardLog(state, "8️⃣ Membuka weave.figma.com untuk capture token...");
    await setWizardState(state);

    const weavyTab = await openTabAndWait("https://weave.figma.com/", 30000);
    weavyTabId = weavyTab.id;
    await new Promise(r => setTimeout(r, 3000));

    // Try to trigger login
    await sendToTab(weavyTabId, { action: "WIZARD_WEAVY_LOGIN" }, 15000);
    await new Promise(r => setTimeout(r, 8000));

    // Check if token was captured
    const tokensBefore = await getTokens();
    const newToken = tokensBefore.find(t => t.email === state.email && !isExpired(t));

    if (newToken) {
      wizardLog(state, `🎯 Token berhasil ditangkap untuk ${state.email}!`);
      
      // Auto-send to server
      const { serverUrl } = await chrome.storage.local.get("serverUrl");
      const sendResult = await sendTokenToServer(newToken, serverUrl || SERVER_URLS[0]);
      if (sendResult.ok) {
        wizardLog(state, "📤 Token berhasil dikirim ke server!");
      } else {
        wizardLog(state, `⚠️ Kirim ke server gagal: ${sendResult.error}`);
      }
    } else {
      wizardLog(state, "⏳ Token belum tertangkap. Mungkin perlu login manual di Weavy.");
    }

    // Close weavy tab
    await closeTabSafely(weavyTabId);
    weavyTabId = null;

    state.completed++;
    state.step = "ROUND_DONE";
    wizardLog(state, `✅ Ronde ${round} selesai!`);
    await setWizardState(state);

  } catch (err) {
    state.failed++;
    state.step = "ERROR";
    wizardLog(state, `❌ Error: ${err.message}`);
    await setWizardState(state);

    // Cleanup tabs
    if (icloudTabId) await closeTabSafely(icloudTabId);
    if (figmaTabId) await closeTabSafely(figmaTabId);
    if (weavyTabId) await closeTabSafely(weavyTabId);
  }
}

async function startWizard(totalRounds, reviveEnabled = true) {
  let state = await getWizardState();
  if (state.running) return { ok: false, error: "Wizard sudah berjalan!" };

  state = {
    running: true,
    step: "STARTING",
    log: [],
    email: null,
    password: null,
    tabId: null,
    totalRequested: totalRounds,
    completed: 0,
    failed: 0,
    currentRound: 0,
    reviveEnabled: reviveEnabled,
  };
  wizardLog(state, `🧙 Wizard dimulai! Target: ${totalRounds} akun (Revive: ${reviveEnabled ? "Aktif" : "Nonaktif"})`);
  await setWizardState(state);

  // Run rounds sequentially
  for (let i = 0; i < totalRounds; i++) {
    state = await getWizardState();
    if (!state.running) {
      wizardLog(state, "🛑 Wizard dihentikan oleh user.");
      await setWizardState(state);
      break;
    }
    await runWizardRound(state);

    // Wait between rounds
    if (i < totalRounds - 1) {
      state = await getWizardState();
      if (!state.running) break;
      wizardLog(state, "⏳ Jeda 5 detik sebelum ronde berikutnya...");
      await setWizardState(state);
      await new Promise(r => setTimeout(r, 5000));
    }
  }

  state = await getWizardState();
  state.running = false;
  state.step = "DONE";
  wizardLog(state, `🏁 Wizard selesai! Berhasil: ${state.completed}, Gagal: ${state.failed}`);
  await setWizardState(state);

  // Send notification
  chrome.notifications.create({
    type: "basic",
    iconUrl: "icons/icon48.png",
    title: "🧙 Wizard Selesai!",
    message: `Berhasil: ${state.completed}, Gagal: ${state.failed} dari ${state.totalRequested} target`,
    priority: 2,
  });

  return { ok: true };
}

async function stopWizard() {
  const state = await getWizardState();
  state.running = false;
  state.step = "STOPPED";
  wizardLog(state, "🛑 Wizard dihentikan.");
  await setWizardState(state);
  return { ok: true };
}

// ─── Message handler (from popup) ──────────────────────────────────────────

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.type === "GET_TOKENS") {
    getTokens().then(sendResponse);
    return true;
  }
  if (msg.type === "SEND_TOKEN") {
    sendTokenToServer(msg.tokenData, msg.serverUrl, msg.password)
      .then(sendResponse);
    return true;
  }
  if (msg.type === "SEND_ALL_UNSENT") {
    (async () => {
      const tokens = await getTokens();
      const unsent = tokens.filter((t) => !t.sent && !isExpired(t));
      const results = [];
      for (const t of unsent) {
        const r = await sendTokenToServer(t, msg.serverUrl, msg.password);
        results.push({ email: t.email, ...r });
      }
      sendResponse({ results });
    })();
    return true;
  }
  if (msg.type === "CLEAR_TOKENS") {
    chrome.storage.local.remove(TOKEN_STORAGE_KEY).then(() => {
      updateBadge();
      sendResponse({ ok: true });
    });
    return true;
  }
  if (msg.type === "UPDATE_BADGE") {
    updateBadge().then(() => sendResponse({ ok: true }));
    return true;
  }

  // ── Wizard messages ──────────────────────────────────────────────────────
  if (msg.type === "WIZARD_START") {
    startWizard(msg.rounds || 1, msg.revive ?? true);
    sendResponse({ ok: true, msg: "Wizard started" });
    return false;
  }
  if (msg.type === "WIZARD_STOP") {
    stopWizard().then(sendResponse);
    return true;
  }
  if (msg.type === "WIZARD_STATUS") {
    getWizardState().then(sendResponse);
    return true;
  }
});

// Init badge on startup
updateBadge();

// Keep-alive port connection to prevent Service Worker suspension
const alivePorts = new Set();
chrome.runtime.onConnect.addListener((port) => {
  if (port.name === "wizard-keepalive") {
    alivePorts.add(port);
    port.onMessage.addListener((msg) => {
      if (msg.type === "ping") {
        try { port.postMessage({ type: "pong" }); } catch (e) {}
      }
    });
    port.onDisconnect.addListener(() => {
      alivePorts.delete(port);
    });
  }
});

