/**
 * auto-token.js — Kling Motion Auto Token Bot (v4 - FINAL)
 *
 * Fixes based on real execution logs + Puppeteer recording:
 * - Step 1/2: WORKING ✅
 * - Step 3: Fixed using page.locator() + frameLocator('iframe') for email iframe
 *   Puppeteer recording shows: click div[2]/div[1] in inbox, then iframe → tr:nth-of-type(3) a
 */

import { chromium } from "playwright";
import * as Accounts from "./account-store.js";
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";

const __dirname = dirname(fileURLToPath(import.meta.url));

const ICLOUD_URL    = "https://icloud-setan.texaproject.com/";
const WEAVY_API   = process.env.WEAVY_API_URL || "https://api.weavy.ai";
const WEAVE_URL   = process.env.WEAVE_URL     || "https://weave.figma.com/";
const DEFAULT_RECIPE_ID = process.env.WEAVY_RECIPE_ID || "V99kxpHSTJRHhTjRsui8hv";
const FIGMA_SIGNUP  = "https://www.figma.com/signup";
const isHeadless      = () => process.env.PLAYWRIGHT_HEADLESS !== "false";
const VOUCHER       = process.env.ICLOUD_SETAN_VOUCHER || "";
const DEFAULT_PASS  = process.env.FIGMA_PASSWORD_DEFAULT || "Tekno@Kling2026!";

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

let activeBrowser = null;

export async function stopActiveBot() {
  if (activeBrowser) {
    try {
      await activeBrowser.close();
      activeBrowser = null;
      return { ok: true, msg: "Bot dihentikan paksa." };
    } catch (e) {
      return { ok: false, error: e.message };
    }
  }
  return { ok: false, error: "Tidak ada bot yang sedang berjalan." };
}

function makeLogger(emit) {
  return (msg, type = "info") => {
    const time = new Date().toLocaleTimeString("id-ID");
    const line = `[${time}] ${msg}`;
    console.log(`[AutoToken] ${line}`);
    if (emit) emit({ type, msg: line, raw: msg });
  };
}

// ────────────────────────────────────────────────────────────────────
// STEP 1: Login voucher, generate email, reserve — closes page after done
//         Returns: { email, password }  ← page is CLOSED
// ────────────────────────────────────────────────────────────────────
export async function generateAndOpenInbox(context, log) {
  log("📧 Step 1: Membuka iCloud Setan...");

  // Diagnosis IP Browser
  try {
    const ipPage = await context.newPage();
    await ipPage.goto("https://api.ipify.org?format=json", { timeout: 10000 });
    const ipJson = await ipPage.textContent("body");
    log(`📡 [Diagnosis] Browser IP aktif: ${ipJson.trim()}`);
    await ipPage.close();
  } catch (e) {
    log(`⚠️ [Diagnosis] Gagal memeriksa IP browser: ${e.message}`, "warn");
  }

  const page = await context.newPage();
  try {
    await page.goto(ICLOUD_URL, { waitUntil: "domcontentloaded", timeout: 30000 });
    await sleep(2000);

    // Login with voucher if overlay is visible
    const loginHidden = await page.locator("#login-overlay").evaluate(
      (el) => el.classList.contains("hidden")
    ).catch(() => true);

    if (!loginHidden) {
      log("🔐 Login dengan voucher...");
      await page.locator("#login-code").fill(VOUCHER);
      await sleep(300);
      await page.locator("#login-submit").click();
      await page.waitForSelector("#btn-generate", { timeout: 15000 });
      log("✅ Login voucher berhasil");
    } else {
      log("ℹ️ Session masih aktif");
    }

    // Get all server options from the #account-picker select element
    log("⏳ Menunggu list server iCloud terpopulasi...");
    await page.waitForSelector("#account-picker option", { timeout: 10000 }).catch(() => {});
    const serverOptions = await page.locator("#account-picker option").evaluateAll(
      (options) => options.map(opt => ({ value: opt.value, text: opt.textContent.trim() }))
    ).catch(() => []);

    log(`ℹ️ Menemukan ${serverOptions.length} server iCloud di picker.`);

    let email = null;
    let selectedIndex = 0;
    let success = false;

    while (selectedIndex < Math.max(1, serverOptions.length)) {
      if (serverOptions.length > 0) {
        const serverOpt = serverOptions[selectedIndex];
        log(`🔄 Memilih ${serverOpt.text} (ID: ${serverOpt.value})...`);
        await page.locator("#account-picker").selectOption(serverOpt.value);
        await sleep(1500);
      }

      log("🎲 Generate email acak...");
      await page.locator("#btn-generate").click();

      // Tunggu respon: sukses (#step-reserve) atau limit/error (#limit-modal / #message)
      let limitReached = false;
      let failReason = "Batas Harian Tercapai (Limit) atau Gagal";

      try {
        await Promise.race([
          page.locator("#step-reserve").waitFor({ state: "visible", timeout: 15000 }),
          page.waitForFunction(() => {
            const modal = document.getElementById("limit-modal");
            const msg = document.getElementById("message");
            const isLimit = modal && !modal.classList.contains("hidden");
            const isError = msg && !msg.classList.contains("hidden") && msg.classList.contains("bg-red-100");
            return isLimit || isError;
          }, { timeout: 15000 })
        ]);

        const isLimitVisible = await page.evaluate(() => {
          const modal = document.getElementById("limit-modal");
          return modal && !modal.classList.contains("hidden");
        });

        const errorMsg = await page.evaluate(() => {
          const msg = document.getElementById("message");
          if (msg && !msg.classList.contains("hidden") && msg.classList.contains("bg-red-100")) {
            return msg.textContent.trim();
          }
          return null;
        });

        if (isLimitVisible || errorMsg) {
          limitReached = true;
          failReason = errorMsg || "Batas Harian Tercapai (Limit)";
          if (isLimitVisible) {
            log("🖱️ Menutup modal limit...");
            await page.locator("#limit-close").click().catch(() => {});
            await sleep(500);
          }
        }
      } catch (raceErr) {
        log(`⚠️ Timeout/Error menunggu respon generate email: ${raceErr.message}`, "warn");
        limitReached = true;
        failReason = `Timeout/Error: ${raceErr.message}`;
      }

      if (limitReached) {
        log(`⚠️ Server ${selectedIndex + 1} limit/gagal: "${failReason}"`, "warn");
        if (serverOptions.length > 0 && selectedIndex < serverOptions.length - 1) {
          selectedIndex++;
          log(`🔄 Beralih ke server berikutnya (Server ${selectedIndex + 1})...`);
          continue;
        } else {
          throw new Error(`Semua server iCloud Setan limit atau gagal. Terakhir: ${failReason}`);
        }
      }

      // Ambil hasil email
      try {
        email = await page.locator("#generated-email").inputValue();
        if (email && email.includes("@icloud.com")) {
          log(`📬 Email berhasil dibuat: ${email}`);
          success = true;
          break; // Keluar dari loop, sukses!
        }
      } catch (err) {
        log(`⚠️ Gagal mengambil email di Server ${selectedIndex + 1}: ${err.message}`, "warn");
        if (serverOptions.length > 0 && selectedIndex < serverOptions.length - 1) {
          selectedIndex++;
          log(`🔄 Beralih ke server berikutnya (Server ${selectedIndex + 1})...`);
          continue;
        } else {
          throw err;
        }
      }
    }

    if (!success || !email) {
      throw new Error("Gagal membuat email iCloud aktif.");
    }

    // Fill label & reserve
    const suffix = Math.random().toString(36).slice(2, 6).toUpperCase();
    await page.locator("#email-label").fill(`Kling-${suffix}`);
    await sleep(200);
    await page.locator("#btn-reserve").click();
    log("⏳ Mengaktifkan email...");
    
    try {
      await Promise.race([
        page.locator("#step-inbox").waitFor({ state: "visible", timeout: 25000 }),
        page.waitForFunction(() => {
          const msg = document.getElementById("message");
          return msg && !msg.classList.contains("hidden") && msg.classList.contains("bg-red-100");
        }, { timeout: 25000 })
      ]);

      const errorMsg = await page.evaluate(() => {
        const msg = document.getElementById("message");
        if (msg && !msg.classList.contains("hidden") && msg.classList.contains("bg-red-100")) {
          return msg.textContent.trim();
        }
        return null;
      });

      if (errorMsg) {
        throw new Error(`Gagal mendaftarkan email iCloud: "${errorMsg}"`);
      }
    } catch (waitErr) {
      const errorMsg = await page.evaluate(() => {
        const msg = document.getElementById("message");
        if (msg && !msg.classList.contains("hidden")) {
          return msg.textContent.trim();
        }
        return null;
      }).catch(() => null);

      if (errorMsg) {
        throw new Error(`Gagal mengaktifkan email: "${errorMsg}"`);
      }
      throw waitErr;
    }
    log("✅ Email aktif & terdaftar di saved list");

    const password = DEFAULT_PASS.replace(/!+$/, "") + suffix + "!";
    log(`🔑 Password Figma: ${password}`);

    // Close page — Step 3 will open a FRESH page
    await page.close();
    return { email, password };
  } catch (err) {
    await page.close().catch(() => {});
    throw err;
  }
}

// ────────────────────────────────────────────────────────────────────
// STEP 2: Register Figma account (separate tab)
// ────────────────────────────────────────────────────────────────────
export async function registerFigmaAccount(context, { email, password }, log) {
  log("📝 Step 2: Daftar akun Figma...");
  const page = await context.newPage();

  try {
    await page.goto(FIGMA_SIGNUP, { waitUntil: "domcontentloaded", timeout: 30000 });
    await sleep(2000);

    // Fill email
    await page.locator("input[type='email'], input[name='email']").first().fill(email);
    await sleep(400);
    log("✅ Email diisi");

    // Click Continue
    for (const sel of [
      "button:has-text('Continue with email')",
      "button:has-text('Continue')",
      "button[type='submit']",
    ]) {
      if (await page.locator(sel).count() > 0) {
        await page.locator(sel).first().click();
        await sleep(2500);
        break;
      }
    }

    // Password if prompted
    if (await page.locator("input[type='password']").count() > 0) {
      await page.locator("input[type='password']").first().fill(password);
      log("✅ Password diisi");
      await sleep(400);
      for (const sel of ["button[type='submit']", "button:has-text('Continue')", "button:has-text('Create')"]) {
        if (await page.locator(sel).count() > 0) {
          await page.locator(sel).first().click();
          await sleep(3000);
          break;
        }
      }
    }

    // Wait for redirect to success page (up to 12 seconds in headless, or 2.5 minutes in headful to allow manual solving)
    const hl = isHeadless();
    const maxWaitSec = hl ? 12 : 150;
    const intervalMs = 1000;
    const maxLoops = Math.ceil((maxWaitSec * 1000) / intervalMs);
    let isSuccess = false;

    if (!hl) {
      log("💡 Jendela browser terbuka. Jika muncul CAPTCHA, silakan selesaikan di browser. Bot akan otomatis melanjutkan setelah sukses.");
    }

    for (let i = 0; i < maxLoops; i++) {
      const url = page.url();
      if (url.includes("verify") || url.includes("files") || url.includes("team") || url.includes("recents")) {
        isSuccess = true;
        break;
      }
      // Check for common error selectors
      const errorLoc = page.locator("[class*='error'], [id*='error'], [role='alert']").first();
      if (await errorLoc.count() > 0) {
        const errorText = await errorLoc.textContent().catch(() => "");
        if (errorText.trim()) {
          log(`❌ Error pada Figma signup: "${errorText.trim()}"`, "error");
          break;
        }
      }
      await sleep(intervalMs);
    }

    if (!isSuccess) {
      const screenshotPath = join(__dirname, "..", "public", "figma-signup-error.png");
      await page.screenshot({ path: screenshotPath }).catch(() => {});
      log(`⚠️ Figma signup tidak mengarah ke halaman verifikasi (URL: ${page.url()}). Screenshot disimpan di /figma-signup-error.png`, "warn");
      throw new Error(`Signup Figma gagal atau tertahan CAPTCHA. Silakan cek http://localhost:3001/figma-signup-error.png`);
    }

    log(`✅ Step 2 selesai (URL: ${page.url()})`);
  } finally {
    await page.close().catch(() => {});
  }
}

// ────────────────────────────────────────────────────────────────────
// STEP 3: Get verification link — EXACT selectors from Puppeteer recording
//
// Recording flow:
//  1. Navigate fresh to icloud-setan.texaproject.com
//  2. Login with voucher if needed
//  3. Click "Lihat Email Tersimpan" → find email → click font-mono span → inbox opens
//  4. Refresh inbox → click message: div.flex-1 > div.flex (XPath: inbox-messages/div/div[1]/div[2]/div[1])
//  5. iframe: mainFrame().childFrames()[0] → tr:nth-of-type(3) a
// ────────────────────────────────────────────────────────────────────
export async function getVerificationLink(context, { email }, log) {
  log("📨 Step 3: Cek inbox untuk email verifikasi Figma...");

  const MAX_WAIT_MS = 6 * 60 * 1000; // 6 minutes
  const POLL_MS     = 12000;
  const start       = Date.now();

  while (Date.now() - start < MAX_WAIT_MS) {
    const elapsed = Math.floor((Date.now() - start) / 1000);
    log(`🔄 Cek inbox (${elapsed}s / ${MAX_WAIT_MS / 1000}s)...`);

    const page = await context.newPage();
    try {
      // ── 1. Fresh navigation ──
      await page.goto("https://icloud-setan.texaproject.com/", {
        waitUntil: "domcontentloaded",
        timeout: 30000,
      });
      await sleep(2000);

      // ── 2. Login with voucher if needed ──
      const loginHidden = await page.locator("#login-overlay")
        .evaluate((el) => el.classList.contains("hidden"))
        .catch(() => true);

      if (!loginHidden) {
        log("🔐 Re-login voucher...");
        await page.locator("#login-code").fill(VOUCHER);
        await page.locator("#login-submit").click();
        await page.waitForSelector("#btn-generate", { timeout: 15000 });
        await sleep(1000);
        log("✅ Re-login berhasil");
      }

      // ── 3. Open saved email list ──
      await page.locator("#btn-show-saved").waitFor({ state: "visible", timeout: 8000 });
      await page.locator("#btn-show-saved").click();
      await page.locator("#step-saved").waitFor({ state: "visible", timeout: 8000 });
      await sleep(2000);

      // ── 4. Find our email and click the CARD via DOM (page.evaluate) ──
      // Using page.evaluate to directly dispatch click on the card element.
      // Playwright's .click() on a child span may not bubble to the card's
      // addEventListener('click', ...) handler properly.
      const cardClicked = await page.evaluate((targetEmail) => {
        // Find the copy button with data-email matching our email
        const copyBtns = document.querySelectorAll(".btn-copy-saved");
        for (const btn of copyBtns) {
          if (btn.dataset.email === targetEmail) {
            // Find the parent card (.bg-white)
            const card = btn.closest(".bg-white") || btn.parentElement;
            if (card) {
              // Dispatch click directly on card
              card.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
              return true;
            }
          }
        }
        // Fallback: try clicking the email text span
        const spans = document.querySelectorAll("span.font-mono, span.text-blue-600");
        for (const span of spans) {
          if (span.textContent.trim() === targetEmail) {
            const card = span.closest(".bg-white") || span.parentElement;
            if (card) {
              card.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
              return true;
            }
          }
        }
        return false;
      }, email);

      if (!cardClicked) {
        log(`⚠️ Email ${email} tidak ada di saved list, tunggu...`);
        await page.close();
        await sleep(POLL_MS);
        continue;
      }
      log(`✅ Card diklik via DOM evaluate: ${email}`);

      // ── 5. Wait for inbox view ──
      // Try both #step-inbox visible AND check if inbox-messages appears
      try {
        await page.locator("#step-inbox").waitFor({ state: "visible", timeout: 15000 });
      } catch {
        // Maybe the inbox is shown differently — check if #inbox-messages is visible
        const inboxMsgVisible = await page.locator("#inbox-messages").isVisible().catch(() => false);
        if (!inboxMsgVisible) {
          log("⚠️ #step-inbox tidak muncul setelah klik card, coba lagi...", "warn");
          await page.close();
          await sleep(POLL_MS);
          continue;
        }
      }
      await sleep(1500);
      log("📬 Inbox view terbuka");

      // ── 6. Refresh inbox with retry for "Memuat pesan..." ──
      // Sometimes the inbox shows "Memuat pesan..." (still loading).
      // Retry refresh up to 3 times (5 seconds each) before giving up.
      let inboxText = "";
      let hasFigma = false;

      for (let r = 0; r < 3; r++) {
        await page.locator("#btn-refresh-inbox").click().catch(() => {});
        log(`🔄 Refresh inbox (attempt ${r + 1}/3)...`);
        await sleep(5000);

        inboxText = await page.locator("#inbox-messages").textContent().catch(() => "");
        hasFigma = inboxText.toLowerCase().includes("figma");

        const isLoading = inboxText.includes("Memuat pesan") || inboxText.trim() === "";
        if (hasFigma) break; // Found!
        if (!isLoading) break; // Definitively empty, no need to retry

        log(`⏳ Inbox masih loading ("${inboxText.trim().substring(0, 40)}"), tunggu...`);
        await sleep(3000); // extra wait before retry
      }

      if (!hasFigma) {
        const preview = inboxText.trim().substring(0, 80) || "(kosong)";
        log(`⏳ Belum ada email Figma. Inbox: "${preview}"`);

        await page.close();
        await sleep(POLL_MS);
        continue;
      }

      log("📧 Email Figma ditemukan di inbox!");

      // ── 8. Click the Figma message ──
      // From Puppeteer recording exact selectors (race):
      // - 'div.flex-1 > div.flex'
      // - XPath: //*[@id="inbox-messages"]/div/div[1]/div[2]/div[1]
      // - '::-p-text(Figma\n      )'
      let clicked = false;

      // Try XPath first (most specific from recording)
      try {
        const xpathEl = page.locator("xpath=//*[@id='inbox-messages']/div/div[1]/div[2]/div[1]");
        if (await xpathEl.count() > 0) {
          await xpathEl.click({ timeout: 8000, offset: { x: 103, y: 18 } });
          clicked = true;
          log("✅ Klik via XPath inbox-messages/div/div[1]/div[2]/div[1]");
        }
      } catch (e) {
        log(`⚠️ XPath click: ${e.message.split("\n")[0]}`, "warn");
      }

      // Fallback: 'div.flex-1 > div.flex' (from recording)
      if (!clicked) {
        try {
          const flexEl = page.locator("#inbox-messages div.flex-1 > div.flex").first();
          if (await flexEl.count() > 0) {
            await flexEl.click({ timeout: 8000, force: true });
            clicked = true;
            log("✅ Klik via div.flex-1 > div.flex");
          }
        } catch (e) {
          log(`⚠️ flex click: ${e.message.split("\n")[0]}`, "warn");
        }
      }

      // Fallback: click text containing "Figma"
      if (!clicked) {
        try {
          await page.locator("#inbox-messages").locator("text=Figma").first()
            .click({ timeout: 5000, force: true });
          clicked = true;
          log("✅ Klik via text=Figma");
        } catch {}
      }

      if (!clicked) {
        log("⚠️ Tidak bisa klik pesan inbox, coba lagi...", "warn");
        await page.close();
        await sleep(POLL_MS);
        continue;
      }

      // Wait longer for email content/iframe to fully load after click
      await sleep(6000);

      // ── 9. Access iframe & get verify link ──
      // From Puppeteer recording: frame = targetPage.mainFrame().childFrames()[0]
      // Then: frame.locator('tr:nth-of-type(3) a')
      let verifyUrl = null;

      // Poll for frames to appear (max 15 seconds)
      let allFrames = [];
      for (let i = 0; i < 5; i++) {
        allFrames = page.frames().filter(f => f !== page.mainFrame());
        if (allFrames.length > 0) {
          log(`🖼️ Frames found: ${allFrames.length} (after ${(i + 1) * 3}s)`);
          break;
        }
        log(`🖼️ childFrames().length = 0, tunggu ${3}s...`);
        await sleep(3000);
      }

      // Method 1: page.frames() — all non-main frames
      if (allFrames.length > 0) {
        for (const frame of allFrames) {
          try {
            await frame.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
            const linkEl = await frame.$(
              "tr:nth-of-type(3) a, " +
              "a[href*='figma.com/verify'], " +
              "a[href*='figma.com/email'], " +
              "a[href*='figma.com/auth'], " +
              "[aria-label='Verify email']"
            );
            if (linkEl) {
              verifyUrl = await linkEl.getAttribute("href");
              if (verifyUrl) {
                log(`🔗 Link dari frame: ${verifyUrl.substring(0, 60)}...`);
                break;
              }
            }
            if (!verifyUrl) {
              const allLinks = await frame.$$eval("a[href]",
                (els) => els.map((e) => e.href).filter((h) => h.includes("figma"))
              );
              if (allLinks.length > 0) {
                verifyUrl = allLinks[0];
                log(`🔗 Link dari frame (all links): ${verifyUrl.substring(0, 60)}...`);
                break;
              }
            }
          } catch {}
        }
      }

      // Method 2: frameLocator('iframe') — Playwright's built-in
      if (!verifyUrl) {
        try {
          // Wait for iframe to appear first
          await page.waitForSelector("iframe", { timeout: 8000 }).catch(() => {});
          const iframeCount = await page.locator("iframe").count();
          log(`🖼️ iframe tags found: ${iframeCount}`);
          for (const sel of [
            "tr:nth-of-type(3) a",
            "a:has-text('Verify email')",
            "a:has-text('Verify')",
            "a[href*='figma.com']",
          ]) {
            const fl = page.frameLocator("iframe").locator(sel).first();
            if (await fl.count().catch(() => 0) > 0) {
              verifyUrl = await fl.getAttribute("href", { timeout: 5000 });
              if (verifyUrl) {
                log(`🔗 Link dari frameLocator (${sel}): ${verifyUrl.substring(0, 60)}...`);
                break;
              }
            }
          }
        } catch (e) {
          log(`⚠️ frameLocator: ${e.message.split("\n")[0]}`);
        }
      }

      // Method 3: page.content() HTML scan
      if (!verifyUrl) {
        try {
          const html = await page.content();
          const m =
            html.match(/href="(https?:\/\/[^"]*figma\.com\/email\/[^"]+)"/) ||
            html.match(/href="(https?:\/\/[^"]*figma\.com[^"]*(?:verify|confirm)[^"]*)"/) ||
            html.match(/(https?:\/\/www\.figma\.com\/[^\s"<>]*(?:verify|confirm)[^\s"<>]*)/);
          if (m) {
            verifyUrl = m[1];
            log(`🔗 Link dari HTML source: ${verifyUrl.substring(0, 60)}...`);
          }
        } catch {}
      }



      await page.close();

      if (verifyUrl) {
        log("✅ Step 3 selesai — link verifikasi didapat!");
        return verifyUrl;
      }

      log("⚠️ Iframe email tidak mengandung link Figma, coba lagi...");
    } catch (err) {
      log(`⚠️ Error: ${err.message.split("\n")[0]}`, "warn");
      await page.close().catch(() => {});
    }

    await sleep(POLL_MS);
  }

  throw new Error(`Timeout ${MAX_WAIT_MS / 1000}s: Email verifikasi tidak muncul.`);
}

// ────────────────────────────────────────────────────────────────────
// STEP 4: Open verification link
// ────────────────────────────────────────────────────────────────────
export async function verifyFigmaEmail(context, verifyUrl, log) {
  log("✔️ Step 4: Buka link verifikasi...");
  const page = await context.newPage();
  try {
    await page.goto(verifyUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
    await sleep(4000);
    log(`✅ Verifikasi selesai (URL: ${page.url()})`);
  } finally {
    await page.close().catch(() => {});
  }
}

// ────────────────────────────────────────────────────────────────────
// STEP 5: Login weave.figma.com + capture Bearer token
//
// Puppeteer recording flow:
//  1. Navigate to https://weave.figma.com/
//  2. Click "Try Now": #try_now_top > div.nav-button-spacer
//  3. Click "Log in with Figma": button:nth-of-type(2) > span
//  4. Figma OAuth auto-completes using existing session
//  5. Capture Bearer token from API requests
// ────────────────────────────────────────────────────────────────────
export async function captureWeavyToken(context, { email, password }, log) {
  log("🌐 Step 5: Login weave.figma.com & tangkap Bearer token...");
  const page = await context.newPage();
  let capturedToken = null;

  // Intercept all requests to capture Authorization header
  page.on("request", (req) => {
    const auth = req.headers()["authorization"] || "";
    if (auth.startsWith("Bearer ")) {
      const token = auth.replace("Bearer ", "").trim();
      if (token.split(".").length === 3 && token.length > 100 && !capturedToken) {
        capturedToken = token;
        log(`🎯 Bearer token ditangkap! (${token.substring(0, 20)}...)`);
      }
    }
  });

  try {
    // ── 1. Navigate to weave.figma.com ──
    log("📍 Navigasi ke weave.figma.com...");
    await page.goto(WEAVE_URL, { waitUntil: "domcontentloaded", timeout: 30000 });
    await sleep(3000);

    const url1 = page.url();
    log(`📍 URL setelah navigate: ${url1}`);

    // If already inside Weave (dashboard), wait for token
    if (url1.includes("weave.figma.com") && !url1.includes("login") &&
        !url1.includes("signin") && url1 !== WEAVE_URL &&
        !url1.endsWith("/")) {
      log("✅ Sudah masuk Weave dashboard!");
      await sleep(8000);
    } else {
      // ── 2. Click "Try Now" button (from Puppeteer recording) ──
      // Selector: #try_now_top > div.nav-button-spacer
      log("🖱️ Klik tombol 'Try Now'...");
      const tryNowClicked = await page.evaluate(() => {
        const el = document.querySelector("#try_now_top > div.nav-button-spacer") ||
                   document.querySelector("#try_now_top .nav-button-spacer") ||
                   document.querySelector("[id*='try'] .nav-button-spacer");
        if (el) { el.click(); return true; }
        return false;
      });

      if (tryNowClicked) {
        log("✅ 'Try Now' diklik");
        await page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
        await sleep(2000);
      } else {
        // Fallback selectors for Try Now
        for (const sel of [
          "#try_now_top > div.nav-button-spacer",
          "#try_now_top .nav-button-spacer",
          "a:has-text('Try now')",
          "a:has-text('Get started')",
          "button:has-text('Try')",
        ]) {
          if (await page.locator(sel).count() > 0) {
            await page.locator(sel).first().click({ timeout: 5000 });
            await page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 10000 }).catch(() => {});
            await sleep(2000);
            log(`✅ Try Now diklik via: ${sel}`);
            break;
          }
        }
      }

      const url2 = page.url();
      log(`📍 URL setelah Try Now: ${url2}`);
      await sleep(1500);

      // ── 3. Click "Log in with Figma" (from Puppeteer recording) ──
      // Selector: button:nth-of-type(2) > span  OR  text 'Log in with Figma'
      log("🖱️ Klik 'Log in with Figma'...");

      let loginClicked = false;

      // Try Puppeteer recording selectors first
      for (const sel of [
        "button:nth-of-type(2) > span",                          // exact from recording
        "[data-testid='signin-page-content'] button:nth-of-type(2)", // XPath parent context
        "button:has-text('Log in with Figma')",
        "button:has-text('Log in with')",
        "button:has-text('Login with Figma')",
        "a:has-text('Log in with Figma')",
      ]) {
        try {
          if (await page.locator(sel).count() > 0) {
            await page.locator(sel).first().click({ timeout: 6000 });
            loginClicked = true;
            log(`✅ 'Log in with Figma' diklik via: ${sel}`);
            await page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 20000 }).catch(() => {});
            await sleep(3000);
            break;
          }
        } catch {}
      }

      if (!loginClicked) {
        log("⚠️ Tombol 'Log in with Figma' tidak ditemukan.", "warn");
      }

      log(`📍 URL setelah login: ${page.url()}`);
    }

    // ── 4. Navigate ke Flow + Create Recipe ──
    let capturedRecipeId = DEFAULT_RECIPE_ID;

    // Intercept ALL POST requests to api.weavy.ai for recipe creation detection
    page.on("request", (req) => {
      const u = req.url();
      if (req.method() === "POST" && u.includes("api.weavy.ai")) {
        log(`📡 POST → ${u.replace("https://api.weavy.ai", "")}`);
      }
      const patterns = [
        /\/batches\/recipes\/([A-Za-z0-9_-]{10,})/,
        /\/api\/v1\/recipes\/([A-Za-z0-9_-]{10,})/,
        /\/flow\/([A-Za-z0-9_-]{10,})/,
      ];
      for (const pat of patterns) {
        const m = u.match(pat);
        if (m && m[1] && m[1] !== DEFAULT_RECIPE_ID && m[1] !== "V99kxpHSTJRHhTjRsui8hv") {
          capturedRecipeId = m[1];
          log(`📋 Recipe ID dari request: ${capturedRecipeId}`);
        }
      }
    });

    page.on("response", async (res) => {
      try {
        const u = res.url();
        const ct = res.headers()["content-type"] || "";
        if (ct.includes("json") && u.includes("api.weavy.ai") && res.request().method() === "POST") {
          const body = await res.json().catch(() => null);
          if (body) {
            const id = body.id || body.recipeId || body.flowId || body.data?.id;
            if (id && typeof id === "string" && id.length > 8 && id !== DEFAULT_RECIPE_ID) {
              capturedRecipeId = id;
              log(`📋 Recipe ID dari response: ${capturedRecipeId}`);
            }
            log(`📥 POST response: ${JSON.stringify(body).substring(0, 150)}`);
          }
        }
      } catch {}
    });

    // Wait for initial load + token (Bearer already captured during OAuth)
    log("⏳ Menunggu Bearer token...");
    await sleep(5000);

    // ── Navigate to Weavy HOME ──
    const KNOWN_RECIPE = "V99kxpHSTJRHhTjRsui8hv";
    let FLOW_TEMPLATE = {};
    try {
      FLOW_TEMPLATE = JSON.parse(readFileSync(join(__dirname, "../data/flow-template.json"), "utf8"));
    } catch {}

    log("🏠 Navigasi ke home page Weavy...");
    await page.goto("https://app.weavy.ai/", {
      waitUntil: "domcontentloaded",
      timeout: 20000,
    }).catch(() => {});
    await sleep(8000);
    log(`📍 Home URL: ${page.url()}`);

    // Log full page text for debugging
    const pageText = await page.evaluate(() => document.body?.innerText?.substring(0, 800)).catch(() => "");
    log(`📄 Page: ${pageText.replace(/\n+/g, " ").substring(0, 400)}`);

    // ── APPROACH 1: Click "New File" or "+" button → paste JSON ──
    if (capturedRecipeId === DEFAULT_RECIPE_ID) {
      log("📄 Mencari tombol 'Create New File'...");
      const newFileSelectors = [
        "button:has-text('Create New File')",  // Exact text seen on Weavy home page
        "text=Create New File",
        "button:has-text('New File')",
        "button:has-text('New file')",
        "button:has-text('New')",
        "button:has-text('New Flow')",
        "[aria-label='Create New File']",
        "[aria-label='New File']",
        "[title='Create New File']",
        "[title='New File']",
      ];

      let newFileClicked = false;
      for (const sel of newFileSelectors) {
        try {
          const el = page.locator(sel).first();
          if (await el.count() > 0) {
            log(`🖱️ Klik New File: ${sel}`);
            await el.click({ timeout: 4000 });
            await sleep(5000);
            log(`📍 URL setelah klik: ${page.url()}`);
            newFileClicked = true;
            break;
          }
        } catch {}
      }

      if (newFileClicked) {
        // ── Paste JSON into the editor/canvas ──
        log("📋 Mencoba paste JSON ke editor...");
        const jsonStr = JSON.stringify(FLOW_TEMPLATE);

        // Try pasting into text area or code editor
        const editorSelectors = [
          "textarea",
          "[contenteditable='true']",
          "[class*='editor'] textarea",
          "[class*='code'] textarea",
          "[class*='json'] textarea",
          ".monaco-editor textarea",
          "[role='textbox']",
        ];

        let pasteDone = false;
        for (const sel of editorSelectors) {
          try {
            const el = page.locator(sel).first();
            if (await el.count() > 0) {
              log(`📝 Paste ke: ${sel}`);
              await el.click({ timeout: 3000 });
              await el.fill(jsonStr);
              pasteDone = true;
              break;
            }
          } catch {}
        }

        if (!pasteDone) {
          // Try clipboard paste on canvas
          log("📋 Coba paste via clipboard ke canvas...");
          await page.evaluate((json) => {
            navigator.clipboard.writeText(json).catch(() => {});
          }, jsonStr);
          await page.keyboard.press("Control+a");
          await sleep(200);
          await page.keyboard.press("Control+v");
          await sleep(3000);
        }

        // Look for Save button after paste
        const saveSelectors = ["button:has-text('Save')", "button:has-text('Create')", "button:has-text('Import')", "button[type='submit']"];
        for (const sel of saveSelectors) {
          try {
            const el = page.locator(sel).first();
            if (await el.count() > 0) {
              log(`💾 Klik Save: ${sel}`);
              await el.click({ timeout: 3000 });
              await sleep(5000);
              break;
            }
          } catch {}
        }

        log(`📍 URL setelah paste+save: ${page.url()}`);
      }
    }

    // ── APPROACH 2: Kling Motion template on home page ──
    if (capturedRecipeId === DEFAULT_RECIPE_ID) {
      log("🖱️ Mencari template Kling Motion...");
      const templateSelectors = [
        "text=Kling Motion Control",
        "text=Kling Motion",
        "button:has-text('Kling')",
        "a:has-text('Kling')",
        "[class*='template']:has-text('Kling')",
        "[class*='flow']:has-text('Kling')",
      ];
      for (const sel of templateSelectors) {
        try {
          const el = page.locator(sel).first();
          if (await el.count() > 0) {
            log(`🖱️ Klik template: ${sel}`);
            await el.click({ timeout: 4000 });
            await sleep(5000);
            log(`📍 URL: ${page.url()}`);
            break;
          }
        } catch {}
      }
    }



    // If template click navigated to a flow page, capture recipe ID from URL
    const currentUrl = page.url();
    const urlMatch = currentUrl.match(/\/flow\/([A-Za-z0-9_-]{10,})/);
    if (urlMatch && urlMatch[1] && urlMatch[1] !== DEFAULT_RECIPE_ID && urlMatch[1] !== KNOWN_RECIPE) {
      capturedRecipeId = urlMatch[1];
      log(`✅ Recipe ID dari URL: ${capturedRecipeId}`);
    } else if (capturedRecipeId === DEFAULT_RECIPE_ID) {
      // Try navigating directly to known flow URL as fallback
      log(`🔗 Fallback: navigasi ke flow/${KNOWN_RECIPE}`);
      await page.goto(`https://app.weavy.ai/flow/${KNOWN_RECIPE}`, {
        waitUntil: "domcontentloaded", timeout: 20000,
      }).catch(() => {});
      await sleep(5000);
      log(`📍 URL: ${page.url()}`);
    }



    // ── Try fork/duplicate via API ──
    if (capturedToken && capturedRecipeId === DEFAULT_RECIPE_ID) {
      log("🔀 Mencoba fork/duplicate recipe via API...");
      const apiHeaders = {
        authorization: `Bearer ${capturedToken}`,
        accept: "application/json, text/plain, */*",
        "content-type": "application/json",
        "x-weavy-auth-provider": "firebase",
        "x-app-version": "4.1.306",
        origin: "https://app.weavy.ai",
        referer: `https://app.weavy.ai/flow/${KNOWN_RECIPE}`,
      };

      const forkEndpoints = [
        { method: "POST", url: `${WEAVY_API}/api/v1/recipes/${KNOWN_RECIPE}/duplicate` },
        { method: "POST", url: `${WEAVY_API}/api/v1/recipes/${KNOWN_RECIPE}/fork` },
        { method: "POST", url: `${WEAVY_API}/api/v1/recipes/${KNOWN_RECIPE}/clone` },
        { method: "POST", url: `${WEAVY_API}/api/v1/recipes/${KNOWN_RECIPE}/copy` },
        { method: "POST", url: `${WEAVY_API}/api/v1/batches/recipes/${KNOWN_RECIPE}/duplicate` },
      ];

      for (const ep of forkEndpoints) {
        if (capturedRecipeId !== DEFAULT_RECIPE_ID) break;
        try {
          log(`🔀 Try: POST ${ep.url.replace("https://api.weavy.ai", "")}`);
          const r = await fetch(ep.url, { method: ep.method, headers: apiHeaders, body: "{}" });
          log(`📡 Status: ${r.status}`);
          if (r.ok) {
            const data = await r.json().catch(() => ({}));
            const id = data.id || data.recipeId || data.data?.id;
            if (id && id.length > 8) {
              capturedRecipeId = id;
              log(`✅ Recipe forked: ${capturedRecipeId}`);
              break;
            }
          }
        } catch (e) {
          log(`⚠️ ${e.message.split("\n")[0]}`);
        }
      }
    }

    // ── Try clicking Duplicate/Save/Fork button on flow page ──
    if (capturedRecipeId === DEFAULT_RECIPE_ID) {
      log("🖱️ Mencari tombol Duplicate/Save di halaman flow...");
      const btnSelectors = [
        "button:has-text('Duplicate')",
        "button:has-text('Save')",
        "button:has-text('Fork')",
        "button:has-text('Clone')",
        "button:has-text('Copy')",
        "button:has-text('Use')",
        "[aria-label='Duplicate']",
        "[aria-label='Save']",
        "[title='Duplicate']",
        "[title='Save']",
        ".duplicate-btn",
        ".save-btn",
      ];

      for (const sel of btnSelectors) {
        try {
          const el = page.locator(sel).first();
          if (await el.count() > 0) {
            log(`🖱️ Klik: ${sel}`);
            await el.click({ timeout: 3000 });
            await sleep(3000);
            log(`📍 URL setelah klik: ${page.url()}`);
            break;
          }
        } catch {}
      }
    }

    // ── Find per-account Recipe ID via Weavy API ──
    if (capturedToken && capturedRecipeId === DEFAULT_RECIPE_ID) {
      log("🔍 Query /api/v1/recipes untuk cek recipes akun ini...");
      const apiHeaders = {
        authorization: `Bearer ${capturedToken}`,
        accept: "application/json, text/plain, */*",
        "x-weavy-auth-provider": "firebase",
        "x-app-version": "4.1.306",
        origin: "https://app.weavy.ai",
      };

      try {
        const resp = await fetch(`${WEAVY_API}/api/v1/recipes`, { headers: apiHeaders });
        if (resp.ok) {
          const data = await resp.json();
          const recipes = Array.isArray(data) ? data : (data.items || data.data || []);
          log(`📋 Total recipes: ${recipes.length}`);
          if (recipes.length > 0) {
            const kl = recipes.find(r => r.name?.toLowerCase().includes("kling")) || recipes[0];
            capturedRecipeId = kl.id;
            log(`✅ Recipe ID: ${capturedRecipeId}`);
          } else {
            log("⚠️ Masih 0 recipes — pakai DEFAULT_RECIPE_ID sebagai fallback");
          }
        }
      } catch (e) {
        log(`⚠️ ${e.message}`);
      }
    }


    await page.close();

    if (!capturedToken) {
      throw new Error("Bearer token tidak tertangkap. Coba jalankan bot lagi.");
    }

    log(`✅ Step 5 selesai! RecipeId: ${capturedRecipeId}`);
    return { token: capturedToken, recipeId: capturedRecipeId };
  } catch (err) {
    await page.close().catch(() => {});
    throw err;
  }
}

// Helper untuk mendapatkan konfigurasi launching browser (termasuk proxy)
function getLaunchOptions(hl) {
  const options = {
    headless: hl,
    args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-blink-features=AutomationControlled"],
    slowMo: 50,
  };

  let hasProxies = false;
  try {
    const proxyFilePath = join(__dirname, "../proxies.txt");
    const content = readFileSync(proxyFilePath, "utf8");
    const lines = content.split(/\r?\n/).map(l => l.trim()).filter(l => l && !l.startsWith("#"));
    if (lines.length > 0) hasProxies = true;
  } catch (e) {}

  if (process.env.PROXY_ENABLED === "true" || hasProxies) {
    let proxySelected = null;

    // Coba baca dari file proxies.txt di root directory terlebih dahulu
    try {
      const proxyFilePath = join(__dirname, "../proxies.txt");
      const content = readFileSync(proxyFilePath, "utf8");
      const lines = content.split(/\r?\n/).map(l => l.trim()).filter(l => l && !l.startsWith("#"));
      if (lines.length > 0) {
        // Pilih satu proxy secara acak dari list
        const randomLine = lines[Math.floor(Math.random() * lines.length)];
        const parts = randomLine.split(":");
        if (parts.length >= 2) {
          const host = parts[0];
          const port = parts[1];
          const username = parts[2] || "";
          const password = parts[3] || "";
          proxySelected = {
            server: `http://${host}:${port}`,
            username,
            password
          };
          console.log(`[Proxy] Menggunakan proxy acak dari proxies.txt: http://${host}:${port}`);
        }
      }
    } catch (e) {
      // file tidak ditemukan atau error baca, lanjut ke fallback .env
    }

    // Jika tidak ada di proxies.txt, gunakan dari .env sebagai fallback
    if (!proxySelected && process.env.PROXY_SERVER) {
      proxySelected = {
        server: process.env.PROXY_SERVER,
        username: process.env.PROXY_USERNAME || "",
        password: process.env.PROXY_PASSWORD || ""
      };
      console.log(`[Proxy] Menggunakan proxy dari .env: ${process.env.PROXY_SERVER}`);
    }

    if (proxySelected) {
      options.proxy = {
        server: proxySelected.server,
      };
      if (proxySelected.username) {
        options.proxy.username = proxySelected.username;
      }
      if (proxySelected.password) {
        options.proxy.password = proxySelected.password;
      }
    }
  }
  return options;
}

// ────────────────────────────────────────────────────────────────────
// REVIVE: Login back into an existing account to capture a fresh token
// ────────────────────────────────────────────────────────────────────
export async function refreshExistingAccountToken(accountId, email, password, onLog = null) {
  const log = makeLogger(onLog);
  let browser = null;

  log(`🔄 Memulai proses revive/login ulang untuk akun: ${email}...`, "start");

  try {
    const hl = isHeadless();
    log(`🌐 Membuka browser (headless: ${hl})...`);
    browser = await chromium.launch(getLaunchOptions(hl));
    activeBrowser = browser;

    const context = await browser.newContext({
      userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
      viewport: { width: 1280, height: 720 },
    });

    // Diagnosis IP Browser
    try {
      const ipPage = await context.newPage();
      await ipPage.goto("https://api.ipify.org?format=json", { timeout: 10000 });
      const ipJson = await ipPage.textContent("body");
      log(`📡 [Diagnosis] Browser IP aktif (Revive): ${ipJson.trim()}`);
      await ipPage.close();
    } catch (e) {
      log(`⚠️ [Diagnosis] Gagal memeriksa IP browser (Revive): ${e.message}`, "warn");
    }

    const page = await context.newPage();
    let capturedToken = null;

    // Intercept Bearer token
    page.on("request", (req) => {
      const auth = req.headers()["authorization"] || "";
      if (auth.startsWith("Bearer ")) {
        const token = auth.replace("Bearer ", "").trim();
        if (token.split(".").length === 3 && token.length > 100 && !capturedToken) {
          capturedToken = token;
          log(`🎯 Bearer token ditangkap! (${token.substring(0, 20)}...)`);
        }
      }
    });

    log("📍 Navigasi ke weave.figma.com...");
    await page.goto(WEAVE_URL, { waitUntil: "domcontentloaded", timeout: 30000 });
    await sleep(3000);

    // Klik Try Now
    log("🖱️ Klik tombol 'Try Now'...");
    let tryNowClicked = await page.evaluate(() => {
      const el = document.querySelector("#try_now_top > div.nav-button-spacer") ||
                 document.querySelector("#try_now_top .nav-button-spacer") ||
                 document.querySelector("[id*='try'] .nav-button-spacer");
      if (el) { el.click(); return true; }
      return false;
    });

    if (!tryNowClicked) {
      for (const sel of [
        "#try_now_top > div.nav-button-spacer",
        "#try_now_top .nav-button-spacer",
        "a:has-text('Try now')",
        "button:has-text('Try')",
      ]) {
        if (await page.locator(sel).count() > 0) {
          await page.locator(sel).first().click({ timeout: 5000 });
          tryNowClicked = true;
          break;
        }
      }
    }
    await sleep(3000);

    // Klik Log in with Figma
    log("🖱️ Klik 'Log in with Figma'...");
    let loginClicked = false;
    for (const sel of [
      "button:nth-of-type(2) > span",
      "button:has-text('Log in with Figma')",
      "button:has-text('Login with Figma')",
    ]) {
      try {
        if (await page.locator(sel).count() > 0) {
          await page.locator(sel).first().click({ timeout: 6000 });
          loginClicked = true;
          break;
        }
      } catch {}
    }
    await sleep(5000);

    // Cek jika Figma meminta email & password (OAuth login page)
    let currentUrl = page.url();
    log(`📍 URL saat ini: ${currentUrl}`);

    // Jika masuk ke Weavy signin page, klik login Figma dulu
    if (currentUrl.includes("weavy.ai/signin") || currentUrl.includes("app.weavy.ai/signin")) {
      log("🖱️ Berada di halaman signin Weavy, klik 'Log in with Figma'...");
      let weavyLoginClicked = false;
      for (const sel of [
        "button:has-text('Log in with Figma')",
        "button:has-text('Login with Figma')",
        "button:nth-of-type(2)",
        "button",
      ]) {
        try {
          const el = page.locator(sel);
          if (await el.count() > 0) {
            await el.first().click({ timeout: 5000 });
            weavyLoginClicked = true;
            log(`✅ Tombol login Weavy diklik via: ${sel}`);
            await sleep(5000);
            break;
          }
        } catch {}
      }
      currentUrl = page.url();
      log(`📍 URL setelah klik login Weavy: ${currentUrl}`);
    }

    if (currentUrl.includes("figma.com")) {
      log("🔐 Melakukan autentikasi di halaman login Figma...");
      
      // Tunggu input email
      await page.locator("input[name='email'], input[type='email']").first().waitFor({ state: "visible", timeout: 15000 });
      await page.locator("input[name='email'], input[type='email']").first().fill(email);
      await sleep(500);

      // Cek password field
      if (await page.locator("input[type='password']").count() > 0) {
        await page.locator("input[type='password']").first().fill(password);
        await sleep(500);
        const submitBtn = page.locator("button[type='submit'], button:has-text('Log in'), button:has-text('Submit')").first();
        await submitBtn.click();
      } else {
        // Step-by-step
        const continueBtn = page.locator("button:has-text('Continue'), button[type='submit']").first();
        await continueBtn.click();
        await sleep(2000);
        
        await page.locator("input[type='password']").first().waitFor({ state: "visible", timeout: 10000 });
        await page.locator("input[type='password']").first().fill(password);
        await sleep(500);
        
        const submitBtn = page.locator("button[type='submit'], button:has-text('Log in'), button:has-text('Continue')").first();
        await submitBtn.click();
      }
      log("⏳ Menunggu verifikasi login figma...");
      await sleep(8000);
    }

    log("⏳ Menunggu Bearer token ter-intercept...");
    await sleep(6000);

    if (!capturedToken) {
      throw new Error("Gagal menangkap Bearer token selama relogin.");
    }

    // Ambil/Query Recipe ID untuk memastikan recipeId valid
    let capturedRecipeId = DEFAULT_RECIPE_ID;
    const apiHeaders = {
      authorization: `Bearer ${capturedToken}`,
      accept: "application/json, text/plain, */*",
      "x-weavy-auth-provider": "firebase",
      origin: "https://app.weavy.ai",
    };

    try {
      const resp = await fetch(`${WEAVY_API}/api/v1/recipes`, { headers: apiHeaders });
      if (resp.ok) {
        const data = await resp.json();
        const recipes = Array.isArray(data) ? data : (data.items || data.data || []);
        if (recipes.length > 0) {
          const kl = recipes.find(r => r.name?.toLowerCase().includes("kling")) || recipes[0];
          capturedRecipeId = kl.id;
          log(`✅ Recipe ID terverifikasi: ${capturedRecipeId}`);
        }
      }
    } catch {}

    // Update di database
    Accounts.updateAccount(accountId, { token: capturedToken, recipeId: capturedRecipeId, expired: false });
    log(`🎉 REVIVE SUKSES! Token baru disimpan untuk ${email}`, "success");

    await browser.close();
    activeBrowser = null;
    return { ok: true, token: capturedToken, recipeId: capturedRecipeId };
  } catch (err) {
    log(`❌ REVIVE GAGAL: ${err.message}`, "error");
    if (browser) await browser.close().catch(() => {});
    activeBrowser = null;
    return { ok: false, error: err.message };
  }
}

// ────────────────────────────────────────────────────────────────────
// MAIN: Full flow orchestration
// ────────────────────────────────────────────────────────────────────
export async function runFullFlow(onLog = null) {
  const log = makeLogger(onLog);
  let browser = null;

  log("🚀 Memulai Auto Token Bot...", "start");

  if (!VOUCHER) {
    const err = "❌ ICLOUD_SETAN_VOUCHER belum diset di .env!";
    log(err, "error");
    return { ok: false, error: err };
  }

  try {
    const hl = isHeadless();
    log(`🌐 Membuka browser (headless: ${hl})...`);
    browser = await chromium.launch(getLaunchOptions(hl));
    activeBrowser = browser;

    const context = await browser.newContext({
      userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
      viewport: { width: 1280, height: 720 },
    });

    // Step 1: Generate email (page closes after done)
    const { email, password } = await generateAndOpenInbox(context, log);

    // Step 2: Register Figma (separate tab, closes after done)
    await registerFigmaAccount(context, { email, password }, log);

    // Wait for Figma to send verification email (typically 30-90s)
    log("⏳ Menunggu email verifikasi Figma dikirim (60 detik)...");
    await sleep(60000);

    // Step 3: Fresh navigation to iCloud Setan inbox — matches Puppeteer recording
    const verifyUrl = await getVerificationLink(context, { email }, log);

    // Step 4: Verify email
    await verifyFigmaEmail(context, verifyUrl, log);
    await sleep(3000);

    // Step 5: Capture token
    const { token, recipeId } = await captureWeavyToken(context, { email, password }, log);

    // Save
    log("💾 Menyimpan token ke pool...");
    const label = email.split("@")[0];
    const acc = Accounts.addAccount({ token, recipeId, label, email, password });

    log(`🎉 SELESAI! Akun baru: ${acc.label} (${acc.id})`, "success");
    await browser.close();
    activeBrowser = null;
    return { ok: true, accountId: acc.id, label: acc.label, email };
  } catch (err) {
    log(`❌ ERROR: ${err.message}`, "error");
    if (browser) await browser.close().catch(() => {});
    activeBrowser = null;
    return { ok: false, error: err.message };
  }
}
