/**
 * auto-token.js — Kling Motion Auto Token Bot (v6 — Figma Sign-In)
 *
 * NEW FLOW:
 *   1. Bot buka figma.com/signup → isi email + password → Submit
 *   2. Verifikasi email Figma via link dari ICLOUD inbox
 *   3. Handle Figma onboarding (skip, free plan)
 *   4. Buka app.weavy.ai/signin → klik "Log in with Figma"
 *   5. Figma OAuth (auto-authorize) → redirect ke Weavy
 *   6. Tangkap Bearer token → simpan ke pool
 *
 * Admin menyediakan daftar email via textarea di dashboard Tools Akun.
 * Bot memproses email satu per satu secara berurutan.
 */

let playwrightModule = null;
async function getPlaywright() {
  if (!playwrightModule) {
    playwrightModule = await import("playwright");
  }
  return playwrightModule;
}
import * as Accounts from "./account-store.js";
import { loadIcloudSettings } from "./icloud-settings.js";
import { readFileSync, existsSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import { browserWeavyFetch } from "./weavy-client-playwright.js";

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

const WEAVY_API        = process.env.WEAVY_API_URL || "https://api.weavy.ai";
const WEAVY_SIGNIN     = "https://app.weavy.ai/signin";
const DEFAULT_RECIPE_ID = process.env.WEAVY_RECIPE_ID || "G6jw67AIcRhGA8eKDF0WEu";
const isHeadless        = () => false; // Paksa headless: false agar jendela browser visual Chrome asli muncul sehingga Turnstile Cloudflare terlewati dan user bisa memantau/mengintervensi jika perlu

// ICLOUD API config (digunakan untuk fetch inbox via server API, bukan Puppeteer)
// Dibaca dari settings JSON (admin dashboard), fallback ke .env
const SERVER_PORT     = process.env.PORT || "3002";

function getIcloudConfig() {
  const s = loadIcloudSettings();
  return {
    apiUrl: s.apiUrl || process.env.ICLOUD_API_URL || `http://localhost:${SERVER_PORT}`,
    session: s.sessionToken || process.env.ICLOUD_SESSION_TOKEN || "",
    device: s.deviceId || process.env.ICLOUD_DEVICE_ID || "",
    account: s.accountId || process.env.ICLOUD_ACCOUNT_ID || "",
  };
}
const OWNER_PASSWORD  = process.env.OWNER_PASSWORD || "4rg0v14N"; // Owner password untuk auth admin endpoint

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 });
  };
}

// ─── CAPTCHA Detection (comprehensive) ─────────────────────────────

async function detectCaptchaChallenge(page) {
  try {
    const url = page.url().toLowerCase();
    if (
      url.includes("captcha") ||
      url.includes("challenge") ||
      url.includes("turnstile") ||
      url.includes("recaptcha") ||
      url.includes("hcaptcha") ||
      url.includes("arkose") ||
      url.includes("funcaptcha") ||
      url.includes("verify")
    ) {
      return true;
    }
    const bodyText = await page.evaluate(() => document.body?.innerText || "");
    const lower = bodyText.toLowerCase();
    const keywords = [
      "captcha", "verify you are human", "i am not a robot", "bukan robot",
      "sedang memverifikasi", "verification", "turnstile", "challenge",
      "press and hold", "tekan dan tahan", "cloudflare", "hcaptcha",
      "arkose", "funcaptcha", "are you human"
    ];
    for (const kw of keywords) {
      if (lower.includes(kw)) return true;
    }
    return false;
  } catch {
    return false;
  }
}

// ─── Stealth Anti-Detection Script ──────────────────────────────────
// Comprehensive hiding of WebDriver/Playwright automation traces
const STEALTH_SCRIPT = `
// 1. Overwrite navigator.webdriver
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });

// 1.1 Override custom window properties to bypass Puppeteer/Playwright headless detection
if (window.Element && window.Element.prototype) {
  const originalGetAttribute = window.Element.prototype.getAttribute;
  window.Element.prototype.getAttribute = function(name) {
    if (name === 'webdriver') return null;
    return originalGetAttribute.apply(this, arguments);
  };
}

// 2. Overwrite chrome.runtime
window.chrome = {
  runtime: { onConnect: { addListener: () => {} }, onMessage: { addListener: () => {} } },
  loadTimes: () => {},
  csi: () => {},
  app: { isInstalled: false, InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' }, RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' } },
};

// 3. Overwrite navigator.plugins (realistic length)
const fakePlugins = [
  { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
  { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
  { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' },
];
Object.defineProperty(navigator, 'plugins', {
  get: () => {
    const arr = fakePlugins.map(p => {
      const plugin = {
        name: p.name, filename: p.filename, description: p.description, length: 1,
        0: { type: '', suffixes: '' },
        namedItem: () => null, item: () => null,
      };
      return plugin;
    });
    arr.item = (i) => arr[i] || null;
    arr.namedItem = () => null;
    arr.refresh = () => {};
    Object.setPrototypeOf(arr, PluginArray.prototype);
    return arr;
  },
});

// 4. Overwrite navigator.languages
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en', 'id'] });

// 5. Overwrite navigator.platform
Object.defineProperty(navigator, 'platform', { get: () => 'Win32' });

// 6. Overwrite navigator.vendor
Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.' });

// 7. Overwrite navigator.hardwareConcurrency (realistic)
Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8 });

// 8. Overwrite navigator.deviceMemory
Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });

// 9. Overwrite permissions
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
  parameters.name === 'notifications' ?
    Promise.resolve({ state: Notification.permission }) :
    originalQuery(parameters)
);

// 10. Remove Playwright-specific properties
delete window.__playwright;
delete window.__pw_manual;
delete window.__PW_inspect;

// 11. Overwrite MouseEvent screenX/screenY properties (emulate human pointer moves)
Object.defineProperty(MouseEvent.prototype, 'screenX', {
  get: function () {
    return this.clientX + (window.screenX || 0);
  }
});
Object.defineProperty(MouseEvent.prototype, 'screenY', {
  get: function () {
    return this.clientY + (window.screenY || 0);
  }
});
`;

// ─── Stealth context factory ────────────────────────────────────────
async function createStealthContext(browser) {
  let dynamicUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
  try {
    const sessionPath = join(__dirname, "../cloudflare/session.json");
    const sessionData = JSON.parse(readFileSync(sessionPath, "utf8"));
    if (sessionData && sessionData.userAgent) {
      dynamicUserAgent = sessionData.userAgent;
    }
  } catch (_) {}

  const context = await browser.newContext({
    userAgent: dynamicUserAgent,
    viewport: { width: 1280, height: 720 },
    locale: "en-US",
    timezoneId: "Asia/Jakarta",
    permissions: ["geolocation", "notifications"],
    geolocation: { latitude: -6.2088, longitude: 106.8456 },
    colorScheme: "light",
    deviceScaleFactor: 1,
    isMobile: false,
    hasTouch: false,
  });
  context.setDefaultTimeout(30000);

  // Inject stealth BEFORE any page script runs (lebih kuat dari page event)
  await context.addInitScript(STEALTH_SCRIPT);

  return context;
}

// ─── Helper: get launch options ─────────────────────────────────────

function getLaunchOptions(hl) {
  const options = {
    headless: hl,
    channel: "chrome", // Gunakan browser Chrome asli untuk meminimalkan deteksi bot oleh Cloudflare
    args: [
      "--no-sandbox",
      "--disable-setuid-sandbox",
      "--disable-blink-features=AutomationControlled",
      "--disable-features=IsolateOrigins,site-per-process",
      "--disable-site-isolation-trials",
      "--disable-web-security",
      "--disable-features=BlockInsecurePrivateNetworkRequests",
      "--disable-features=OutOfBlinkCors",
      "--disable-component-update",
      "--disable-domain-reliability",
      "--disable-sync",
      "--no-default-browser-check",
      "--no-first-run",
      "--disable-default-apps",
      "--disable-popup-blocking",
      "--disable-prompt-on-repost",
      "--disable-hang-monitor",
      "--disable-client-side-phishing-detection",
      "--disable-infobars",
      "--disable-breakpad",
      "--disable-dev-shm-usage",
      "--mute-audio",
      "--window-size=1280,720",
      "--start-maximized",
      "--disable-blink-features", // Reset flag blink agar tidak ketahuan automation
    ],
    slowMo: 100, // Tambah slowMo agar interaksi penulisan input dan penantian rendering lebih natural
  };

  // Proxy support
  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;
    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) {
        const randomLine = lines[Math.floor(Math.random() * lines.length)];
        const parts = randomLine.split(":");
        if (parts.length >= 2) {
          proxySelected = {
            server: `http://${parts[0]}:${parts[1]}`,
            username: parts[2] || "",
            password: parts[3] || ""
          };
        }
      }
    } catch (_e) {}
    if (!proxySelected && process.env.PROXY_SERVER) {
      proxySelected = {
        server: process.env.PROXY_SERVER,
        username: process.env.PROXY_USERNAME || "",
        password: process.env.PROXY_PASSWORD || ""
      };
    }
    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;
}

// ─── Step 1: Fetch Google OOB Verification Code via ICLOUD API ──────
//
//   MENGGUNAKAN ICLOUD API LANGSUNG (via server endpoint), BUKAN Puppeteer.
//   API List:   GET  /api/inbox/universal
//   API Thread: POST /api/inbox/universal/thread
//
//   JAUH LEBIH CEPAT & RELIABLE daripada klik-klik UI iframe.

async function fetchGoogleVerificationCode(targetEmail, log, skipThreadIds = []) {
  log(`📨 Polling ICLOUD API untuk kode verifikasi ${targetEmail}...`);
  const skipIds = new Set(Array.isArray(skipThreadIds) ? skipThreadIds : []);

  // Helper ekstrak kode (sama dengan di server.js)
  function extractCode(text) {
    if (!text) return null;
    const patterns = [
      /G[-\s]*(\d{6})\b/i,
      /(\d{6})\s+is\s+(?:your|the)\s+(?:Google|verification)\s+code/i,
      /(?:Google|verification)\s+code\s+(?:is\s+)?(\d{6})\b/i,
      /one.?time\s+(?:verification\s+)?code\s*(?:is)?\s*[:：]\s*([A-Z0-9]{6})\b/i,
      /verification\s+code\s*(?:is)?\s*[:：]\s*([A-Z0-9]{6})\b/i,
      /code\s*(?:is)?\s*[:：]\s*([A-Z0-9]{6})\b/i,
    ];
    for (const p of patterns) {
      const m = text.match(p);
      if (m && m[1] && /^[A-Z0-9]{6}$/.test(m[1])) {
        const upper = m[1].toUpperCase();
        if (["GOOGLE","VERIFY","CLOUD","PLEASE","SECURE","ACCESS","SIGNIN"].includes(upper)) continue;
        return upper;
      }
    }
    const idx = text.toLowerCase().search(/verification|one.?time\s+code|google\s+code/i);
    if (idx >= 0) {
      const snippet = text.substring(idx, idx + 350);
      const mDigit = snippet.match(/\b(\d{6})\b/);
      if (mDigit && mDigit[1]) return mDigit[1];
      const m6 = snippet.match(/\b([A-Z0-9]{6})\b/);
      if (m6 && m6[1] && !["GOOGLE","VERIFY","CLOUD","PLEASE","SECURE","ACCESS","SIGNIN"].includes(m6[1])) return m6[1];
    }
    const mAnyDigit = text.match(/\b(\d{6})\b/);
    if (mAnyDigit && mAnyDigit[1]) return mAnyDigit[1];
    return null;
  }

  // Opsi 1: Panggil server endpoint local (lebih simpel, 1 HTTP call)
  try {
    const resp = await fetch(`http://localhost:${SERVER_PORT}/api/admin/icloud/find-code`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-admin-password": OWNER_PASSWORD,
      },
      body: JSON.stringify({ email: targetEmail, skipThreadIds: [...skipIds] }),
    });
    const data = await resp.json();
    if (data.ok && data.code) {
      log(`🎯 KODE DITEMUKAN via server API: ${data.code} (attempt ${data.attempt}, thread ${data.threadId || '?'})`);
      return data.code;
    }
  } catch (e) {
    log(`⚠️ Server API gagal: ${e.message}, fallback ke direct ICLOUD API...`, "warn");
  }

  // Opsi 2: Direct ICLOUD API call (fallback)
  const cfg = getIcloudConfig();
  if (!cfg.session) {
    throw new Error("ICLOUD_SESSION_TOKEN tidak di-set (via Admin Dashboard atau .env)");
  }

  const headers = {
    Authorization: `Bearer ${cfg.session}`,
    "x-device-id": cfg.device,
    "x-account-id": cfg.account,
    "Content-Type": "application/json",
  };

  const maxAttempts = 30;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    await sleep(3000);
    log(`🔍 Direct API poll #${attempt + 1}...`);

    try {
      // 1. Thread list
      const listResp = await fetch(`${cfg.apiUrl}/api/inbox/universal`, { headers });
      const listData = await listResp.json();

      // Deteksi session invalid — hentikan polling segera
      if (listData.success === false || listData.error) {
        log(`❌ ICLOUD API Error: ${listData.error || listData.message || "session invalid"}`, "error");
        break;
      }

      let threads = listData.data || listData || [];
      if (!Array.isArray(threads) || !threads.length) continue;

      // ── SORT DESC + SKIP attempted threads ──
      threads = threads
        .filter(t => !skipIds.has(t.id || t.threadId))
        .sort((a, b) => {
          const ad = new Date(a.date || a.timestamp || 0).getTime();
          const bd = new Date(b.date || b.timestamp || 0).getTime();
          return bd - ad;
        });

      if (!threads.length) continue;

      // 2. Match thread (newest first)
      const searchStr = (t) => [t.subject, t.to, t.from, t.text].join(" ").toLowerCase();
      const emailLower = targetEmail.toLowerCase();
      const matched = threads.find(t => searchStr(t).includes(emailLower))
        || threads.find(t => searchStr(t).includes("google") || searchStr(t).includes("verification"));

      if (!matched) continue;

      // 3. Thread content
      const threadResp = await fetch(`${cfg.apiUrl}/api/inbox/universal/thread`, {
        method: "POST",
        headers,
        body: JSON.stringify({ threadId: matched.id || matched.threadId }),
      });
      const threadData = await threadResp.json();
      const msgs = threadData.data || threadData || [];
      const msgArray = Array.isArray(msgs) ? msgs : (msgs.messages || []);
      const rawContent = msgArray.map((m) => m.text || m.body || m.html || "").join("\n");
      const content = rawContent.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();

      if (!content || !content.toLowerCase().includes(emailLower)) continue;

      // 4. Extract code using shared helper
      const code = extractCode(content);
      if (code) {
        log(`🎯 KODE DITEMUKAN via direct API: ${code}`);
        return code;
      }
    } catch (e) {
      log(`⚠️ Direct API poll #${attempt + 1} error: ${e.message}`, "warn");
    }
  }

  throw new Error(`Kode verifikasi tidak ditemukan untuk ${targetEmail} setelah ${maxAttempts * 3} detik.`);
}

// ─── Step 2.5: Sign-in ke Figma dengan email + password ─────────────
//
//   Login ke figma.com untuk mendapatkan session Figma,
//   lalu session ini dipakai untuk OAuth ke Weavy.
//   Menangani OTP verifikasi Figma via iCloud inbox.

async function fetchFigmaOtpCode(email, log) {
  log(`📨 Polling ICLOUD API untuk kode OTP Figma...`);

  // Helper: deteksi false positive (sama dengan di server.js)
  function isFalsePositive(code) {
    if (!code) return true;
    const blacklist = [
      "000000", "111111", "222222", "333333", "444444", "555555",
      "666666", "777777", "888888", "999999", "123456", "654321",
    ];
    if (blacklist.includes(code)) return true;
    if (/^(\d{2})\1\1$/.test(code)) return true; // 232323, 121212, etc
    if (code.length === 6 && code.substring(0, 3) === code.substring(3, 6)) return true; // 123123
    return false;
  }
  
  const maxAttempts = 30;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    await sleep(3000);
    if (attempt % 3 === 0) log(`🔍 Poll OTP Figma #${attempt + 1}...`);

    try {
      const resp = await fetch(`http://localhost:${SERVER_PORT}/api/admin/icloud/find-code`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-admin-password": OWNER_PASSWORD,
        },
        body: JSON.stringify({ email, skipThreadIds: [], searchKeyword: "figma" }),
      });
      const data = await resp.json();

      if (data.ok) {
        // API udah ekstrak kode — langsung pakai
        if (data.code) {
          log(`🎯 Kode OTP Figma ditemukan: ${data.code}`);
          return data.code;
        }

        // Fallback: cari di rawContent (old format)
        if (data.rawContent) {
          const raw = data.rawContent;
          // Cari semua kandidat 6-7 digit, ambil yang TERAKHIR
          const allMatches = [...raw.matchAll(/\b(\d{6,7})\b/g)];
          for (let i = allMatches.length - 1; i >= 0; i--) {
            const candidate = allMatches[i][1];
            if (!isFalsePositive(candidate)) {
              log(`🎯 Kode OTP Figma ditemukan: ${candidate}`);
              return candidate;
            }
          }
        }
      }
    } catch (e) {
      log(`⚠️ Poll OTP #${attempt + 1} error: ${e.message}`, "warn");
    }
  }

  // Fallback: direct ICLOUD API
  const cfg = getIcloudConfig();
  if (cfg.session) {
    const headers = {
      Authorization: `Bearer ${cfg.session}`,
      "x-device-id": cfg.device,
      "x-account-id": cfg.account,
      "Content-Type": "application/json",
    };

    for (let attempt = 0; attempt < 10; attempt++) {
      await sleep(4000);
      try {
        const listResp = await fetch(`${cfg.apiUrl}/api/inbox/universal`, { headers });
        const listData = await listResp.json();
        let threads = listData.data || listData || [];
        if (!Array.isArray(threads) || !threads.length) continue;

        threads = threads.sort((a, b) => {
          const ad = new Date(a.date || a.timestamp || 0).getTime();
          const bd = new Date(b.date || b.timestamp || 0).getTime();
          return bd - ad;
        });

        const figmaThread = threads.find(t => {
          const s = [t.subject, t.from, t.to].join(" ").toLowerCase();
          return s.includes("figma") || s.includes("authentication");
        });

        if (figmaThread) {
          const threadResp = await fetch(`${cfg.apiUrl}/api/inbox/universal/thread`, {
            method: "POST", headers,
            body: JSON.stringify({ threadId: figmaThread.id || figmaThread.threadId }),
          });
          const threadData = await threadResp.json();
          const msgs = threadData.data || threadData || [];
          const msgArray = Array.isArray(msgs) ? msgs : (msgs.messages || []);
          const raw = msgArray.map((m) => m.text || m.body || m.html || "").join("\n");
          const content = raw.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();

          const codeMatch = content.match(/\b(\d{6,7})\b/);
          if (codeMatch && codeMatch[1] && !isFalsePositive(codeMatch[1])) {
            log(`🎯 Kode OTP Figma via direct API: ${codeMatch[1]}`);
            return codeMatch[1];
          }
          // Fallback: cari semua kandidat, ambil terakhir
          const allMatches = [...content.matchAll(/\b(\d{6,7})\b/g)];
          for (let i = allMatches.length - 1; i >= 0; i--) {
            if (!isFalsePositive(allMatches[i][1])) {
              log(`🎯 Kode OTP Figma via direct API (last): ${allMatches[i][1]}`);
              return allMatches[i][1];
            }
          }
        }
      } catch (e) {
        log(`⚠️ Direct OTP poll #${attempt + 1} error: ${e.message}`, "warn");
      }
    }
  }

  throw new Error("Kode OTP Figma tidak ditemukan di inbox.");
}

async function signInFigma(email, password, page, log) {
  log(`🔐 Login ke Figma: ${email}...`);

  // ── 1. Navigasi ke figma.com, klik "Log in" button ──
  log("📍 Navigasi ke figma.com...");
  await page.goto("https://www.figma.com/", { waitUntil: "domcontentloaded", timeout: 30000 });
  await sleep(4000);

  // Klik "Log in" button di header (top-right)
  log("🖱️ Klik 'Log in' di header...");
  let loginClicked = false;
  for (const sel of [
    "a:has-text('Log in')",
    "a[href*='login']",
    "::-p-text(Log in)",
  ]) {
    try {
      const loc = page.locator(sel);
      if (await loc.count() > 0) {
        await loc.first().click();
        log(`✅ 'Log in' diklik via: ${sel}`);
        loginClicked = true;
        break;
      }
    } catch (_e) {}
  }

  if (!loginClicked) {
    // Coba navigasi langsung ke login
    log("⚠️ Tombol 'Log in' tidak ditemukan, coba figma.com/login...");
    await page.goto("https://www.figma.com/login", { waitUntil: "domcontentloaded", timeout: 30000 });
  }

  await sleep(4000);

  // ── 2. Form login Figma ada di IFRAME ──
  log("🔍 Mencari iframe form login Figma...");
  let frame = null;

  // Tunggu iframe muncul
  for (let i = 0; i < 15; i++) {
    const frames = page.frames();
    for (const f of frames) {
      if (f !== page.mainFrame()) {
        try {
          const hasEmail = await f.locator("#email").count();
          if (hasEmail > 0) {
            frame = f;
            log(`✅ Iframe login Figma ditemukan!`);
            break;
          }
        } catch (_e) {}
      }
    }
    if (frame) break;
    await sleep(1000);
  }

  if (!frame) {
    // Fallback: coba isi di main page
    log("⚠️ Iframe tidak ditemukan, coba form di main page...");
    frame = page;
  }

  // ── 3. Isi email di dalam iframe ──
  log(`📝 Mengisi email: ${email}...`);
  let emailFilled = false;
  for (const sel of ["#email", "input[type='email']", "input[name='email']"]) {
    try {
      const loc = frame.locator(sel);
      if (await loc.count() > 0) {
        await loc.first().click();
        await sleep(200);
        await loc.first().fill(email);
        log(`✅ Email diisi via: ${sel}`);
        emailFilled = true;
        break;
      }
    } catch (_e) {}
  }

  if (!emailFilled) throw new Error("Input email tidak ditemukan di form login Figma (iframe).");

  await sleep(1000);

  // ── 4. Klik "Continue with email" ──
  log("🖱️ Klik 'Continue with email'...");
  let continueClicked = false;
  for (const sel of [
    "button:has-text('Continue with email')",
    "::-p-text(Continue with email)",
    "button[type='submit']",
  ]) {
    try {
      const loc = frame.locator(sel);
      if (await loc.count() > 0) {
        await loc.first().click();
        log(`✅ Continue via: ${sel}`);
        continueClicked = true;
        break;
      }
    } catch (_e) {}
  }

  if (!continueClicked) {
    // Fallback: cari button dengan teks Continue
    const buttons = await frame.locator("button");
    const cnt = await buttons.count();
    for (let i = 0; i < cnt; i++) {
      const text = (await buttons.nth(i).textContent()).toLowerCase();
      if (text.includes("continue") || text.includes("email") || text.includes("log in")) {
        await buttons.nth(i).click();
        log(`✅ Continue via button index ${i}: "${text}"`);
        continueClicked = true;
        break;
      }
    }
  }

  await sleep(3000);

  // ── 5. Isi password (#current-password di iframe) ──
  log(`📝 Mengisi password...`);
  let passFilled = false;
  for (const sel of [
    "#current-password",
    "input[type='password']",
    "input[name='password']",
    "#password",
  ]) {
    try {
      const loc = frame.locator(sel);
      if (await loc.count() > 0) {
        await loc.first().click();
        await sleep(200);
        await loc.first().fill(password);
        log(`✅ Password diisi via: ${sel}`);
        passFilled = true;
        break;
      }
    } catch (_e) {}
  }

  if (!passFilled) {
    log("⚠️ Input password tidak ditemukan.", "warn");
  }

  await sleep(500);

  // ── 6. PROACTIVE OTP: Mulai polling iCloud SEBELUM submit login ──
  //     Strategi: start iCloud fetch IMMEDIATELY di background SEBELUM submit,
  //     supaya kode OTP punya waktu maksimum untuk sampai di inbox.
  log("📨 Memulai polling iCloud untuk kode OTP Figma (background)...");
  const otpPromise = fetchFigmaOtpCode(email, log).catch((err) => {
    log(`⚠️ Background OTP fetch gagal: ${err.message}`, "warn");
    return null;
  });

  // ── 7. Submit form login di IFRAME ──
  //     PRIORITAS: klik button submit di iframe, BUKAN page.keyboard
  //     Rekaman Lighthouse: setelah iframe submit, main page → figma.com/login?is_not_gen_0=true
  log("🖱️ Submit form login di iframe...");
  let formSubmitted = false;

  // 7a. Cari dan klik button "Log in" / submit di dalam iframe
  for (const sel of [
    "button[type='submit']",
    "button:has-text('Log in')",
    "::-p-text(Log in)",
    "button:has-text('Continue')",
    "button",
  ]) {
    try {
      const loc = frame.locator(sel);
      if (await loc.count() > 0) {
        await loc.first().click({ timeout: 3000 });
        log(`✅ Form submit via iframe button: ${sel}`);
        formSubmitted = true;
        break;
      }
    } catch (_e) {}
  }

  if (!formSubmitted) {
    // 7b. Tekan Enter DI DALAM iframe (bukan page)
    log("⌨️ Fallback: tekan Enter di iframe...");
    try {
      // Klik ulang input password untuk pastiin fokus di iframe
      const passInput = frame.locator("#current-password");
      if (await passInput.count() > 0) {
        await passInput.first().click();
        await sleep(200);
      }
      await frame.locator("body").press("Enter");
      log("✅ Enter ditekan di iframe.");
      formSubmitted = true;
    } catch (_e) {}
  }

  if (!formSubmitted) {
    // 7c. Last-resort: page.keyboard
    log("⌨️ Last-resort: tekan Enter di page...");
    await page.keyboard.press("Enter");
  }

  // Polling URL/page untuk deteksi halaman OTP Figma
  // ── STRATEGI BARU (rekaman 23-Jun-2026):
  //     OTP modal (statsig-comp-without-redux) muncul di figma.com/ BUKAN di URL terpisah.
  //     Statsig component bisa ada di MAIN page ATAU nested frame (frame[8]).
  //     Deteksi PRIMER: cari statsig di SEMUA frame. URL check hanya sekunder.
  let otpPageDetected = false;
  let statsigFrame = null; // frame tempat statsig ditemukan
  const MAX_POLL_ITERATIONS = 25;
  const POLL_INTERVAL_MS = 2000;

  // Helper: cari statsig component di SEMUA frame
  async function findStatsigInAnyFrame(pg) {
    // Cek main frame dulu
    try {
      const cnt = await pg.locator("[data-testid='statsig-comp-without-redux']").count().catch(() => 0);
      if (cnt > 0) return pg.mainFrame();
    } catch (_) {}
    // Cek semua child frames
    for (const f of pg.frames()) {
      if (f === pg.mainFrame()) continue;
      try {
        const cnt = await f.locator("[data-testid='statsig-comp-without-redux']").count().catch(() => 0);
        if (cnt > 0) return f;
      } catch (_) {}
    }
    return null;
  }

  for (let i = 0; i < MAX_POLL_ITERATIONS; i++) {
    await sleep(POLL_INTERVAL_MS);

    const currentUrl = page.url();

    // ── PRIMER: Cari statsig component di SEMUA frame ──
    try {
      statsigFrame = await findStatsigInAnyFrame(page);
      if (statsigFrame) {
        otpPageDetected = true;
        const frameType = statsigFrame === page.mainFrame() ? "main" : "child";
        log(`🎯 Statsig OTP component terdeteksi #${i + 1} (${frameType} frame)`);
        break;
      }
    } catch (_e) {}

    // ── SEKUNDER: Cek URL OTP pattern ──
    if (currentUrl.includes("is_not_gen_0=true") ||
        (currentUrl.includes("/login?") && currentUrl.includes("not_gen")) ||
        currentUrl.includes("verify") || currentUrl.includes("two-factor") || currentUrl.includes("2fa")) {
      otpPageDetected = true;
      log(`🎯 URL OTP terdeteksi #${i + 1}: ${currentUrl.substring(0, 80)}`);
      break;
    }

    // ── TERSIER: Cek body text untuk "Authentication code" ──
    try {
      const bodyText = await page.evaluate(() => document.body?.innerText || "").catch(() => "");
      if (bodyText.includes("Authentication code") || bodyText.includes("verification code") ||
          bodyText.includes("Enter the 6-digit") || bodyText.includes("Enter the code")) {
        otpPageDetected = true;
        log(`🎯 "Authentication code" text terdeteksi #${i + 1}`);
        break;
      }
      // Cek juga di dalam frame
      for (const f of page.frames()) {
        if (f === page.mainFrame()) continue;
        try {
          const fText = await f.evaluate(() => document.body?.innerText || "").catch(() => "");
          if (fText.includes("Authentication code") || fText.includes("Enter the code")) {
            otpPageDetected = true;
            log(`🎯 "Authentication code" text di child frame #${i + 1}`);
            break;
          }
        } catch (_) {}
      }
      if (otpPageDetected) break;
    } catch (_e) {}

    // ── Dashboard detection — HANYA jika statsig TIDAK ditemukan ──
    //     Butuh 3x berturut-turut di URL dashboard tanpa ada statsig
    //     URL dashboard = figma.com/files, figma.com/@, atau figma.com/ tanpa login
    const isDashboardUrl = (
      currentUrl.includes("figma.com/files") ||
      currentUrl.includes("figma.com/@") ||
      currentUrl.includes("/drafts") ||
      (currentUrl.match(/figma\.com\/?(\?.*)?$/) && !currentUrl.includes("login") && !currentUrl.includes("not_gen"))
    );
    if (isDashboardUrl && !statsigFrame) {
      dashboardIterations = (dashboardIterations || 0) + 1;
      if (dashboardIterations >= 3) {
        log(`📍 URL #${i + 1}: dashboard terkonfirmasi (${dashboardIterations}x) — tidak ada OTP.`);
        break;
      }
    } else {
      dashboardIterations = 0;
    }

    if (i % 3 === 0) {
      log(`⏳ Poll OTP page #${i + 1}: ${currentUrl.substring(0, 60)}...`);
    }
  }

  if (otpPageDetected) {
    log("🔐 Figma meminta kode OTP (Authentication code)! Menunggu kode dari iCloud...");

    // Await kode OTP dari iCloud (sudah di-fetch di background)
    const otpCode = await otpPromise;
    if (!otpCode) {
      log("❌ Gagal mengambil kode OTP dari iCloud.", "error");
      throw new Error("Kode OTP Figma tidak ditemukan di inbox iCloud.");
    }
    log(`🔑 Kode OTP siap: ${otpCode}`);

    // ── Pilih frame target: statsigFrame (jika ditemukan), fallback ke page ──
    const targetFrame = statsigFrame || page;
    const frameLabel = statsigFrame && statsigFrame !== page.mainFrame() ? "child frame" : "main page";

    // Isi OTP code di frame tempat statsig component berada
    let otpEntered = false;

    // Tunggu statsig input muncul di frame target
    for (let i = 0; i < 10; i++) {
      const hasInput = await targetFrame.locator("[data-testid='statsig-comp-without-redux'] input").count().catch(() => 0);
      if (hasInput > 0) {
        log(`✅ Statsig OTP input terdeteksi di ${frameLabel}.`);
        break;
      }
      await sleep(1000);
    }

    // Selector dari Lighthouse recording (23-Jun-2026):
    //   aria/Authentication code, input,
    //   xpath///*[@data-testid="statsig-comp-without-redux"]/div/div[1]/div/div/div/form/div[2]/input
    //   (catatan: ada extra /div dibanding rekaman sebelumnya)
    for (const sel of [
      "::-p-aria(Authentication code)",
      "[data-testid='statsig-comp-without-redux'] input",
      "[data-testid='statsig-comp-without-redux'] div input",
      "[data-testid='statsig-comp-without-redux'] div div input",
      "input[aria-label*='Authentication' i]",
      "input[type='tel']",
      "input[type='text']",
      "input:not([type='hidden']):not([type='submit']):not([type='password']):not([type='email'])",
    ]) {
      try {
        const loc = targetFrame.locator(sel);
        if (await loc.count() > 0) {
          const isVisible = await loc.first().isVisible().catch(() => false);
          if (isVisible) {
            await loc.first().click();
            await sleep(300);
            await loc.first().fill("");
            await sleep(100);
            await loc.first().fill(otpCode);
            log(`✅ OTP "${otpCode}" diisi via: ${sel} (${frameLabel})`);
            otpEntered = true;
            break;
          }
        }
      } catch (_e) {
        // continue next selector
      }
    }

    if (!otpEntered) {
      // Last-resort: cari input visible yang bisa diisi di target frame
      try {
        const inputs = targetFrame.locator("input:visible");
        const cnt = await inputs.count();
        for (let i = 0; i < cnt; i++) {
          const type = await inputs.nth(i).getAttribute("type").catch(() => "");
          if (type !== "hidden" && type !== "submit" && type !== "password" && type !== "email") {
            await inputs.nth(i).click();
            await sleep(200);
            await inputs.nth(i).fill(otpCode);
            log(`✅ OTP via input visible index ${i} (${frameLabel})`);
            otpEntered = true;
            break;
          }
        }
      } catch (_e) {}
    }

    if (!otpEntered) {
      // Last-last resort: coba di main page juga
      if (targetFrame !== page && targetFrame !== page.mainFrame()) {
        log("⚠️ Gagal isi di child frame, coba main page...");
        for (const sel of [
          "::-p-aria(Authentication code)",
          "[data-testid='statsig-comp-without-redux'] input",
          "input[type='tel']",
        ]) {
          try {
            const loc = page.locator(sel);
            if (await loc.count() > 0 && await loc.first().isVisible().catch(() => false)) {
              await loc.first().click();
              await sleep(200);
              await loc.first().fill(otpCode);
              log(`✅ OTP "${otpCode}" diisi via main page: ${sel}`);
              otpEntered = true;
              break;
            }
          } catch (_e) {}
        }
      }
    }

    if (otpEntered) {
      await sleep(1000);

      // Klik "Log in" button — dari rekaman:
      //   aria/Log in, button,
      //   xpath///*[@data-testid="statsig-comp-without-redux"]/div/div[1]/div/div/div/form/button
      log("🖱️ Klik 'Log in' setelah OTP...");
      let submitClicked = false;
      for (const sel of [
        "[data-testid='statsig-comp-without-redux'] button",
        "[data-testid='statsig-comp-without-redux'] button > span",
        "button > span",
        "::-p-aria(Log in)",
        "button:has-text('Log in')",
        "::-p-text(Log in)",
        "button[type='submit']",
      ]) {
        try {
          // Coba di targetFrame dulu, lalu di page
          for (const ctx of [targetFrame, page]) {
            const loc = ctx.locator(sel);
            if (await loc.count() > 0) {
              await loc.first().click();
              log(`✅ Log in diklik via: ${sel}`);
              submitClicked = true;
              break;
            }
          }
          if (submitClicked) break;
        } catch (_e) {}
      }

      if (!submitClicked) {
        log("⌨️ Fallback: tekan Enter...");
        await page.keyboard.press("Enter");
      }

      // Tunggu navigasi setelah klik Log in
      // Rekaman: setelah klik → navigasi ke figma.com/files/team/.../recents-and-sharing
      await sleep(6000);
      log(`📍 URL setelah OTP submit: ${page.url().substring(0, 80)}...`);
    } else {
      log("⚠️ Input OTP tidak ditemukan di semua frame.", "warn");
    }
  } else {
    log("✅ Tidak ada OTP terdeteksi — login langsung.");
  }

  // ── 8. Handle onboarding jika muncul ──
  for (let i = 0; i < 3; i++) {
    try {
      const skipBtn = page.locator("button:has-text('Skip'), a:has-text('Skip'), button:has-text('Do this later')");
      if (await skipBtn.count() > 0 && await skipBtn.first().isVisible().catch(() => false)) {
        await skipBtn.first().click({ timeout: 1000 }).catch(() => {});
        log("✅ Skip onboarding diklik.");
        await sleep(1500);
      }
    } catch (_e) {}
  }

  log(`✅ Login Figma berhasil: ${email}`);
  return true;
}

// ─── Step 3: Sign-in ke Weavy via Figma ─────────────────────────────
//
//   Alur:
//     1. Buka app.weavy.ai/signin (LANGSUNG, skip weave.figma.com)
//     2. Klik "Log in with Figma" (button ke-2 di signin)
//     3. Figma OAuth redirect (auto-authorize — session Figma sudah aktif)
//     4. Redirect ke Weavy dashboard
//     5. Tangkap Bearer token dari request API Weavy

async function signInWeavyViaFigma(context, log) {
  log(`🔐 Sign-in ke Weavy via Figma...`);
  const page = await context.newPage();
  let capturedToken = null;

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

  page.on("pageerror", (err) => {
    log(`⚠️ Browser Page Error: ${err.message}`, "warn");
  });
  page.on("console", (msg) => {
    if (msg.type() === "error") {
      log(`⚠️ Browser Console Error: ${msg.text()}`, "warn");
    }
  });

  // Force inject cf_clearance cookie to api.weavy.ai to prevent CORS 403
  try {
    const sessionPath = join(__dirname, "../cloudflare/session.json");
    if (existsSync(sessionPath)) {
      const sessionData = JSON.parse(readFileSync(sessionPath, "utf8"));
      if (sessionData && sessionData.cookieString) {
        const cfCookieString = sessionData.cookieString;
        await page.route("https://api.weavy.ai/**", async (route) => {
          const req = route.request();
          const currentHeaders = req.headers();
          let newCookie = cfCookieString;
          if (currentHeaders["cookie"]) {
            newCookie = `${currentHeaders["cookie"]}; ${cfCookieString}`;
          }
          await route.continue({
            headers: {
              ...currentHeaders,
              "cookie": newCookie
            }
          });
        });
        log("🛡️ Playwright route interceptor: Force inject cf_clearance cookie to api.weavy.ai");
      }
    }
  } catch (errRoute) {
    log(`⚠️ Gagal memasang route interceptor Cloudflare: ${errRoute.message}`, "warn");
  }

  try {
    // Suntikkan cookies Cloudflare bypass dari session.json jika tersedia
    try {
      const sessionPath = join(__dirname, "../cloudflare/session.json");
      const sessionData = JSON.parse(readFileSync(sessionPath, "utf8"));
      if (sessionData && sessionData.cookies) {
        const cfCookies = sessionData.cookies.map(c => ({
          name: c.name,
          value: c.value,
          domain: c.domain,
          path: c.path,
          secure: c.secure,
          httpOnly: c.httpOnly,
          sameSite: c.sameSite === "None" ? "None" : (c.sameSite || "Lax")
        }));
        await context.addCookies(cfCookies);
        log("🔑 Berhasil menyuntikkan cf_clearance cookie dari cloudflare/session.json!");
      }
    } catch (errSession) {
      log(`⚠️ Penyiapan cookie Cloudflare bypass dilewati: ${errSession.message}`, "warn");
    }

    // ── 1. Buka LANGSUNG app.weavy.ai/signin ──
    log("📍 Navigasi ke app.weavy.ai/signin...");
    await page.goto(WEAVY_SIGNIN, { waitUntil: "domcontentloaded", timeout: 30000 });
    
    log("⏳ Menunggu halaman dimuat atau verifikasi Cloudflare...");
    
    // Tunggu tombol Figma ATAU iframe Cloudflare Turnstile muncul (di DOM)
    let resolvedSelector = "";
    try {
      resolvedSelector = await Promise.race([
        page.waitForSelector("iframe", { state: "attached", timeout: 15000 }).then(() => "iframe_found"),
        page.waitForSelector("button:has-text('Figma'), button:has-text('continue'), span:has-text('Figma'), a:has-text('Figma')", { timeout: 15000 }).then(() => "figma")
      ]);
    } catch (e) {
      log(`⚠️ Penantian halaman awal berakhir: ${e.message}`, "warn");
      const frames = page.frames();
      log(`📡 Total frames on page: ${frames.length}. URLs: ${frames.map(f => f.url()).join(", ")}`);
    }

    // Cek keberadaan frame Cloudflare Turnstile secara real-time
    const cfFrame = page.frames().find(f => f.url().includes("challenges.cloudflare.com"));

    if (cfFrame || resolvedSelector === "iframe_found") {
      log("⚠️ Cloudflare Turnstile terdeteksi! Mencoba klik otomatis...", "warn");
      
      // Tunggu iframe termuat penuh & coba klik checkbox otomatis
      await sleep(4000);
      try {
        const activeCfFrame = page.frames().find(f => f.url().includes("challenges.cloudflare.com"));
        if (activeCfFrame) {
          log("🛡️ Menemukan iframe Cloudflare Turnstile! Menganalisis elemen untuk mengklik...");
          
          let clicked = false;
          
          // Dapatkan bounding box langsung dari frameElement
          const frameEl = await activeCfFrame.frameElement();
          const box = frameEl ? await frameEl.boundingBox() : null;
          if (box) {
            log(`🎯 Koordinat iframe Cloudflare Turnstile ditemukan via frameElement: x=${box.x}, y=${box.y}, w=${box.width}, h=${box.height}`);
            // Cari koordinat checkbox Turnstile secara presisi di dalam frame
            let clickX = Math.round(box.x + 35 + Math.random() * 6 - 3);
            let clickY = Math.round(box.y + 32 + Math.random() * 6 - 3);
            
            try {
              // Cari elemen input checkbox di dalam Turnstile secara dinamis
              const checkboxBox = await activeCfFrame.evaluate(() => {
                const el = document.querySelector('input[type="checkbox"]') || document.querySelector('.ctp-checkbox-container') || document.querySelector('#cf-stage');
                if (el) {
                  const rect = el.getBoundingClientRect();
                  return { x: rect.left, y: rect.top, w: rect.width, h: rect.height };
                }
                return null;
              });
              if (checkboxBox) {
                clickX = Math.round(box.x + checkboxBox.x + checkboxBox.w / 2);
                clickY = Math.round(box.y + checkboxBox.y + checkboxBox.h / 2);
                log(`🎯 Koordinat checkbox presisi terdeteksi di dalam frame: x=${clickX}, y=${clickY}`);
              }
            } catch (_) {}

            log(`🎯 Menggerakkan mouse ke koordinat klik: x=${clickX}, y=${clickY}...`);
            
            let clickSuccess = false;
            for (let retry = 0; retry < 6; retry++) {
              if (retry > 0) {
                log(`🔄 Mencoba klik ulang Turnstile (Percobaan #${retry + 1})...`);
              }
              
              // Pindahkan mouse secara bertahap
              await page.mouse.move(box.x + 5, box.y + 5);
              await sleep(100 + Math.random() * 100);
              await page.mouse.move(clickX, clickY, { steps: 5 });
              await sleep(200 + Math.random() * 200);
              
              // Kirim klik langsung via mouse objek
              await page.mouse.down();
              await sleep(50 + Math.random() * 50);
              await page.mouse.up();
              log(`⚡ Klik mouse simulasi manusia dikirim ke Turnstile di: x=${clickX}, y=${clickY}`);

              // Coba kirim klik alternatif menggunakan interaksi event pointer dalam frame
              try {
                await activeCfFrame.locator("body").click({ position: { x: 35, y: 32 }, timeout: 2000 }).catch(() => {});
                log("⚡ Klik paralel via frame locator dikirim.");
              } catch (_) {}
              
              // Tunggu sejenak dan periksa apakah verifikasi telah dilewati
              await sleep(4000);
              const figmaBtn = page.locator("button:has-text('Figma'), button:has-text('continue'), span:has-text('Figma'), a:has-text('Figma')");
              const hasButton = await figmaBtn.count() > 0;
              
              if (hasButton) {
                log("✅ Turnstile berhasil dilewati (tombol Figma terdeteksi)!");
                clickSuccess = true;
                break;
              }
            }
            clicked = clickSuccess;
          }
          
          if (!clicked) {
            // Fallback klik body relatif
            const body = activeCfFrame.locator("body");
            await body.click({ position: { x: 35, y: 32 }, timeout: 5000 }).catch(() => {});
            log("⚡ Klik otomatis fallback koordinat ke body Turnstile (x:35, y:32) dikirim.");
          }
        }
      } catch (errCf) {
        log(`⚠️ Percobaan klik otomatis Cloudflare gagal: ${errCf.message}`, "warn");
      }

      // Tunggu hingga tombol Figma muncul (tunggu 60 detik)
      log("⏳ Menunggu verifikasi Cloudflare selesai...");
      await page.waitForSelector("button:has-text('Figma'), button:has-text('continue'), span:has-text('Figma'), a:has-text('Figma')", { timeout: 60000 }).catch((e) => {
        log(`❌ Cloudflare gagal dilewati setelah 60000ms: ${e.message}`, "error");
      });
    } else {
      log("✅ Halaman login Weavy termuat langsung.");
    }

    await sleep(2000); // Jeda tambahan untuk stabilitas rendering

    log(`📍 URL: ${page.url().substring(0, 80)}...`);

    // ── 2. Klik "Log in with Figma/System" ──
    log("🖱️ Mencari dan mengklik tombol OAuth (Figma/System)...");
    let figmaClicked = false;

    // Cara 1: Scan semua button, link, role=button yang mengandung kata kunci Figma / System
    const searchTerms = ["figma", "system"];
    const selectors = ["button", "a", "[role='button']", "[class*='button']"];
    
    for (const sel of selectors) {
      try {
        const loc = page.locator(sel);
        const count = await loc.count();
        for (let i = 0; i < count; i++) {
          const text = (await loc.nth(i).textContent().catch(() => "")).trim().toLowerCase();
          if (searchTerms.some(term => text.includes(term))) {
            log(`🎯 Menemukan target OAuth via ${sel}: "${text}"`);
            await loc.nth(i).scrollIntoViewIfNeeded().catch(() => {});
            await loc.nth(i).click({ timeout: 5000 });
            log(`✅ Berhasil mengklik tombol OAuth: "${text}"`);
            figmaClicked = true;
            break;
          }
        }
      } catch (_e) {}
      if (figmaClicked) break;
    }

    // Cara 2: Klik button ke-2 (index 1) di signin-page-content (Metode Lama)
    if (!figmaClicked) {
      try {
        const buttons = page.locator("[data-testid='signin-page-content'] button");
        const cnt = await buttons.count();
        log(`🔍 Ada ${cnt} button di signin-page-content.`);
        if (cnt >= 2) {
          await buttons.nth(1).click();
          log(`✅ Klik button index 1 dari ${cnt} via [data-testid='signin-page-content']`);
          figmaClicked = true;
        } else if (cnt === 1) {
          await buttons.nth(0).click();
          log(`✅ Klik satu-satunya button via [data-testid='signin-page-content']`);
          figmaClicked = true;
        }
      } catch (_e) {
        log(`⚠️ Error klik button by index: ${_e.message}`);
      }
    }

    if (!figmaClicked) {
      // Fallback lama
      for (const sel of [
        "button:has-text('Log in with Figma')",
        "button:has-text('Continue with Figma')",
        "button:has-text('Figma')",
      ]) {
        try {
          const loc = page.locator(sel);
          if (await loc.count() > 0) {
            await loc.first().click();
            log(`✅ Figma diklik via fallback: ${sel}`);
            figmaClicked = true;
            break;
          }
        } catch (_e) {}
      }
    }

    if (!figmaClicked) {
      try {
        await page.screenshot({ path: "weavy_error_screenshot.png" });
        log("📸 Screenshot halaman error Weavy disimpan ke weavy_error_screenshot.png");
      } catch (errScreen) {
        log(`⚠️ Gagal mengambil screenshot error: ${errScreen.message}`, "warn");
      }
      throw new Error("Tombol 'Log in with Figma/System' tidak ditemukan di signin page.");
    }

    // ── 3. Tunggu Figma OAuth → mungkin auto-redirect ──
    log("⏳ Menunggu Figma OAuth / redirect ke dashboard...");
    
    let tokenTimeout = 0;
    for (let i = 0; i < 50; i++) {
      await sleep(2000);
      tokenTimeout += 2;

      const url = page.url();
      
      // Jika Figma minta authorize, klik Allow
      if (url.includes("figma.com/oauth")) {
        log("📍 Figma OAuth page — klik Allow...");
        for (const sel of [
          "button:has-text('Allow')",
          "button:has-text('Allow access')",
          "button[type='submit']",
          "button",
        ]) {
          try {
            const loc = page.locator(sel);
            if (await loc.count() > 0) {
              await loc.first().click();
              log(`✅ Allow via: ${sel}`);
              await sleep(3000);
              break;
            }
          } catch (_e) {}
        }
      }

      if (i % 5 === 0 && i > 0) {
        log(`⏳ ${tokenTimeout}s | URL: ${url.substring(0, 50)}... | token: ${capturedToken ? "YES" : "no"}`);
      }

      if (capturedToken && url.includes("app.weavy.ai") && !url.includes("signin")) {
        log("✅ Token didapat dan berada di dashboard Weavy. Menunggu 8 detik agar workspace di-init...");
        await sleep(8000);
        break;
      }
    }

    await page.close().catch(() => {});

    if (!capturedToken) {
      try {
        await page.screenshot({ path: "weavy_error_screenshot.png" });
        log("📸 Screenshot of stuck Weavy page saved to weavy_error_screenshot.png");
      } catch (errScreen) {
        log(`⚠️ Gagal mengambil screenshot: ${errScreen.message}`, "warn");
      }
      throw new Error("Bearer token tidak ditemukan setelah login Figma (timeout 100s).");
    }

    log(`✅ Token didapat: ${capturedToken.substring(0, 25)}...`);
    return { token: capturedToken };
  } catch (err) {
    await page.close().catch(() => {});
    throw err;
  }
}

// ─── Recipe management: ensure Kling 2.6 recipe exists ──────────────

export async function ensureKling26Recipe(token, recipeId, onLog = null) {
  const log = makeLogger(onLog);
  const defaultId = DEFAULT_RECIPE_ID;

  // Cek apakah recipe sudah ada di akun ini
  if (recipeId) {
    try {
      const check = await browserWeavyFetch(`${WEAVY_API}/api/v1/recipes/${recipeId}`, {
        headers: { 
          Authorization: `Bearer ${token}`, 
          "Content-Type": "application/json",
        },
      });
      if (check.status === 200) {
        log(`✅ Recipe ${recipeId} sudah ada.`);
        return recipeId;
      }
    } catch (_e) {}
  }

  // Coba pakai recipe ID default
  if (defaultId && defaultId !== recipeId) {
    try {
      const check = await browserWeavyFetch(`${WEAVY_API}/api/v1/recipes/${defaultId}`, {
        headers: { 
          Authorization: `Bearer ${token}`, 
          "Content-Type": "application/json",
        },
      });
      if (check.status === 200) {
        log(`✅ Recipe default ${defaultId} tersedia.`);
        return defaultId;
      }
    } catch (_e) {}
  }

  // Buat recipe baru jika belum ada
  log("📝 Membuat recipe Kling 2.6 baru via API...");

  try {
    // Ambil workspaceId dari Weavy
    let workspaceId = null;
    try {
      const wsRes = await browserWeavyFetch(`${WEAVY_API}/api/v1/workspaces`, {
        headers: { 
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/json",
          "x-weavy-auth-provider": "firebase",
          "x-app-version": "4.1.577",
        },
      });
      if (wsRes.ok) {
        const wsData = await wsRes.json();
        workspaceId = wsData.id || wsData.workspaceId;
        log(`💼 Workspace ID ditemukan: ${workspaceId}`);
      } else {
        const wsText = await wsRes.text();
        log(`⚠️ Gagal mengambil workspaceId (${wsRes.status}): ${wsText.substring(0, 100)}`, "warn");
      }
    } catch (wsErr) {
      log(`⚠️ Error mengambil workspaceId: ${wsErr.message}`, "warn");
    }

    // Load template
    const templatePath = join(__dirname, "../data/flow-template.json");
    let template;
    try {
      const raw = readFileSync(templatePath, "utf8");
      template = JSON.parse(raw);
    } catch (_e) {
      log(`⚠️ Gagal membaca flow-template.json, buat recipe default.`, "warn");
    }

    const payload = {
      workspaceId,
      name: "Kling Motion TEKNO v2.6",
      description: "Auto-generated Kling 2.6 Standard recipe",
      nodes: template?.nodes || [],
      edges: template?.edges || [],
      modelService: "replicate",
      modelId: "kwaivgi/kling-v2.6-motion-control",
    };

    const createRes = await browserWeavyFetch(`${WEAVY_API}/api/v1/recipes/create`, {
      method: "POST",
      headers: { 
        Authorization: `Bearer ${token}`, 
        "Content-Type": "application/json",
        "x-weavy-auth-provider": "firebase",
        "x-app-version": "4.1.577",
      },
      body: JSON.stringify(payload),
    });

    if (createRes.ok) {
      const data = await createRes.json();
      const newId = data.id || data.recipeId;
      log(`✅ Recipe baru dibuat: ${newId}`);
      return newId;
    }

    const createErrText = await createRes.text();
    log(`⚠️ Gagal membuat recipe (${createRes.status}): ${createErrText.substring(0, 200)}`, "warn");
    return null; // fallback null to prevent infinite loops
  } catch (err) {
    log(`❌ Gagal membuat recipe: ${err.message}`, "error");
    return null;
  }
}

// ─── SINGLE ACCOUNT FLOW ────────────────────────────────────────────

export async function runFullFlow(onLog = null) {
  const log = makeLogger(onLog);
  log("🚀 runFullFlow() tidak digunakan lagi. Gunakan runBulkFlow(emails, onLog).", "warn");
  return { ok: false, error: "runFullFlow deprecated — gunakan runBulkFlow" };
}

// ─── FIGMA SIGN-UP + WEAVY LOGIN (1 akun baru) ─────────────────────
//
// DUA TAHAP TERPISAH:
//   Phase 1 — Daftar akun Figma via figma.com/signup:
//     → Isi email + password → Submit
//     → Verifikasi email via link dari ICLOUD inbox
//     → Handle Figma onboarding (skip, free plan)
//
//   Phase 2 — Login ke Weavy via Figma:
//     Buka app.weavy.ai/signin → Login with Figma
//     → Figma OAuth (auto-authorize) → Tangkap Bearer token
//
//   Password: Tekno@Kling2026 + random 4-char suffix

function generateFigmaPassword() {
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let suffix = "";
  for (let i = 0; i < 4; i++) suffix += chars.charAt(Math.floor(Math.random() * chars.length));
  return `Tekno@Kling2026${suffix}`;
}

// Fetch Figma verification link from iCloud inbox
async function fetchFigmaVerificationLink(email, log) {
  log(`📨 Polling ICLOUD API untuk link verifikasi Figma...`);

  const cfg = getIcloudConfig();

  // Priority 1: HME API inbox polling via remote cPanel (using admin credentials)
  log("📡 Menggunakan HME API inbox polling via cPanel...");
  const hmeHeaders = {
    Authorization: `Bearer ${OWNER_PASSWORD}`,
    "Content-Type": "application/json",
  };

  for (let attempt = 0; attempt < 25; attempt++) {
    await sleep(6000);
    log(`🔍 Poll Figma link (HME API) #${attempt + 1}...`);
    try {
      const resp = await fetch(`${cfg.apiUrl}/api/inbox`, {
        method: "POST",
        headers: hmeHeaders,
        body: JSON.stringify({ hmeEmail: email }),
      });
      const result = await resp.json();

      if (result.success && Array.isArray(result.data) && result.data.length > 0) {
        const figmaMsg = result.data.find(msg => {
          const s = [msg.subject, msg.from, msg.to].join(" ").toLowerCase();
          return s.includes("figma") || s.includes("verification") || s.includes("authentication");
        });
        if (figmaMsg) {
          const rawContent = `${figmaMsg.text || ""} ${figmaMsg.html || ""}`;
          const content = rawContent.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
          const linkMatch =
            content.match(/https?:\/\/[^\s"<>]*figma\.com\/email\/[^\s"<>]+/i) ||
            content.match(/https?:\/\/[^\s"<>]*figma\.com[^\s"<>]*(?:verify|confirm|email)[^\s"<>]*/i);
          if (linkMatch) {
            log(`🎯 Link verifikasi Figma ditemukan via HME API!`);
            return linkMatch[0];
          }
        }
      }
    } catch (e) {
      log(`⚠️ Poll HME API #${attempt + 1} error: ${e.message}`, "warn");
    }
  }

  // Fallback ke server API find-code lokal
  log("📡 Fallback ke local server find-code API...");
  const maxAttempts = 15;
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    await sleep(6000);
    log(`🔍 Fallback Poll Figma link #${attempt + 1}...`);

    try {
      const resp = await fetch(`http://localhost:${SERVER_PORT}/api/admin/icloud/find-code`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-admin-password": OWNER_PASSWORD,
        },
        body: JSON.stringify({
          email,
          skipThreadIds: [],
          searchKeyword: "figma",
        }),
      });
      const data = await resp.json();

      if (data.ok && data.rawContent) {
        const raw = data.rawContent;
        const linkMatch =
          raw.match(/https?:\/\/[^\s"<>]*figma\.com\/email\/[^\s"<>]+/i) ||
          raw.match(/https?:\/\/[^\s"<>]*figma\.com[^\s"<>]*(?:verify|confirm|email)[^\s"<>]*/i);
        if (linkMatch) {
          log(`🎯 Link verifikasi Figma ditemukan via server: ${linkMatch[0].substring(0, 80)}...`);
          return linkMatch[0];
        }
      }
    } catch (e) {
      log(`⚠️ Fallback Poll #${attempt + 1} error: ${e.message}`, "warn");
    }
  }

  throw new Error("Gagal mendapatkan link verifikasi Figma setelah beberapa percobaan.");
}

async function signUpFigma(email, password, context, log) {
  const page = await context.newPage();

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

    if (await detectCaptchaChallenge(page)) {
      log("⚠️ CAPTCHA terdeteksi di halaman signup Figma!", "captcha");
    }

    // ── 2. Isi email ──
    log(`📝 Mengisi email: ${email}...`);
    let emailFilled = false;
    for (const sel of [
      "input[type='email']",
      "input[name='email']",
      "input[placeholder*='Email' i]",
      "input[placeholder*='email' i]",
      "#email",
    ]) {
      try {
        const loc = page.locator(sel);
        if (await loc.count() > 0) {
          await loc.first().click();
          await sleep(200);
          await loc.first().fill(email);
          log(`✅ Email diisi via: ${sel}`);
          emailFilled = true;
          break;
        }
      } catch (_e) {}
    }

    if (!emailFilled) throw new Error("Input email tidak ditemukan di form signup Figma.");

    await sleep(1000);

    // ── 3. Klik Continue ──
    log("🖱️ Klik Continue...");
    let continueClicked = false;
    for (const sel of [
      "button[type='submit']",
      "button:has-text('Continue')",
      "button:has-text('Create account')",
      "button:has-text('Sign up')",
      "button:has-text('Get started')",
    ]) {
      try {
        const loc = page.locator(sel);
        if (await loc.count() > 0) {
          const text = (await loc.first().textContent()).toLowerCase();
          if (!text.includes("google") && !text.includes("saml") && !text.includes("sso")) {
            await loc.first().click();
            log(`✅ Continue diklik via: ${sel}`);
            continueClicked = true;
            break;
          }
        }
      } catch (_e) {}
    }

    if (!continueClicked) {
      // Fallback: klik button pertama yang bukan Google
      const buttons = await page.locator("button");
      const cnt = await buttons.count();
      for (let i = 0; i < cnt; i++) {
        const text = (await buttons.nth(i).textContent()).toLowerCase();
        if (!text.includes("google") && !text.includes("saml")) {
          await buttons.nth(i).click();
          log(`✅ Continue via button index ${i}`);
          continueClicked = true;
          break;
        }
      }
    }

    await sleep(3000);

    // ── 4. Isi password ──
    log(`📝 Mengisi password...`);
    let passFilled = false;
    for (const sel of [
      "input[type='password']",
      "input[name='password']",
      "input[placeholder*='Password' i]",
      "#password",
    ]) {
      try {
        const loc = page.locator(sel);
        if (await loc.count() > 0) {
          await loc.first().click();
          await sleep(200);
          await loc.first().fill(password);
          log(`✅ Password diisi via: ${sel}`);
          passFilled = true;
          break;
        }
      } catch (_e) {}
    }

    if (passFilled) {
      await sleep(1000);

      // ── 5. Klik Create account ──
      log("🖱️ Klik Create account...");
      for (const sel of [
        "button[type='submit']",
        "button:has-text('Create account')",
        "button:has-text('Sign up')",
        "button:has-text('Continue')",
        "button:has-text('Log in')",
      ]) {
        try {
          const loc = page.locator(sel);
          if (await loc.count() > 0) {
            const text = (await loc.first().textContent()).toLowerCase();
            if (!text.includes("google") && !text.includes("saml") && !text.includes("sso")) {
              await loc.first().click();
              log(`✅ Create account diklik via: ${sel}`);
              break;
            }
          }
        } catch (_e) {}
      }
    }

    // ── 6. Verifikasi email Figma via link dari inbox ──
    log("📨 Menunggu email verifikasi Figma...");
    log("⏳ Jeda 15 detik (nunggu email verifikasi terkirim)...");
    await sleep(15000);

    const verifyLink = await fetchFigmaVerificationLink(email, log);
    log(`🔗 Membuka link verifikasi Figma: ${verifyLink.substring(0, 80)}...`);

    // Buka link verifikasi di tab baru
    const verifyPage = await context.newPage();
    await verifyPage.goto(verifyLink, { waitUntil: "domcontentloaded", timeout: 30000 });
    await sleep(5000);
    log(`📍 URL setelah verifikasi: ${verifyPage.url().substring(0, 80)}...`);

    // ── 7. Handle Figma onboarding ──
    // Cek apakah perlu isi nama
    try {
      const nameInput = verifyPage.locator("input[placeholder*='name'], input[type='text']");
      if (await nameInput.count() > 0) {
        const val = await nameInput.first().inputValue().catch(() => "");
        if (!val.trim()) {
          await nameInput.first().fill("Kling User");
          log("📝 Nama diisi: Kling User");
        }
        const continueBtn = verifyPage.locator("button:has-text('Continue')");
        if (await continueBtn.count() > 0 && await continueBtn.first().isVisible().catch(() => false)) {
          await continueBtn.first().click({ timeout: 1500 }).catch(() => {});
          log("✅ Onboarding Continue diklik.");
          await sleep(2000);
        }
      }
    } catch (_e) {}

    // Skip questions
    for (let i = 0; i < 3; i++) {
      try {
        const skipBtn = verifyPage.locator("button:has-text('Skip'), a:has-text('Skip'), button:has-text('Do this later')");
        if (await skipBtn.count() > 0 && await skipBtn.first().isVisible().catch(() => false)) {
          await skipBtn.first().click({ timeout: 1000 }).catch(() => {});
          log("✅ Skip onboarding diklik.");
          await sleep(1500);
        }
      } catch (_e) {}
    }

    // Start for free
    try {
      const freeBtn = verifyPage.locator("button:has-text('Start for free'), span:has-text('Start for free'), button:has-text('Choose starter')");
      if (await freeBtn.count() > 0 && await freeBtn.first().isVisible().catch(() => false)) {
        await freeBtn.first().click({ timeout: 1500 }).catch(() => {});
        log("✅ Free plan dipilih.");
        await sleep(2000);
      }
    } catch (_e) {}

    await verifyPage.close().catch(() => {});
    await page.close().catch(() => {});

    log("✅ [Phase 1] Akun Figma berhasil dibuat!");
    return true;
  } catch (err) {
    await page.close().catch(() => {});
    throw err;
  }
}

export async function signUpFigmaAndLoginWeavy(email, onLog = null) {
  const log = makeLogger(onLog);
  let browser = null;

  const password = generateFigmaPassword();
  log(`🆕 [Phase 1/2] Mendaftar akun Figma: ${email}`, "start");

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

    const context = await createStealthContext(browser);

    // ═══════════════════════════════════════════════════════════════════
    // PHASE 1: DAFTAR AKUN FIGMA
    // ═══════════════════════════════════════════════════════════════════
    await signUpFigma(email, password, context, log);

    // ═══════════════════════════════════════════════════════════════════
    // PHASE 2: LOGIN KE WEAVY VIA FIGMA
    // ═══════════════════════════════════════════════════════════════════
    log(`🔐 [Phase 2/2] Login ke Weavy via Figma...`);

    // Context browser masih sama, session Figma masih aktif
    const { token } = await signInWeavyViaFigma(context, log);

    log(`✅ [Phase 2] Login Weavy berhasil! Token: ${token.substring(0, 25)}...`, "success");
    return { token, password };
  } catch (err) {
    log(`❌ Signup gagal: ${err.message}`, "error");
    throw err;
  } finally {
    if (browser) {
      await browser.close().catch(() => {});
      activeBrowser = null;
    }
  }
}

// ─── BULK ACCOUNT FLOW (email list dari admin) ─────────────────────

/**
 * @param {string[]} emails - Daftar email yang akan diproses
 * @param {function} onLog - Callback SSE untuk log real-time
 * @returns {Promise<{ok: boolean, results: object[], total: number, success: number, failed: number}>}
 */
export async function runBulkFlow(accounts, onLog = null) {
  const log = makeLogger(onLog);
  let browser = null;
  const results = [];
  let success = 0;
  let failed = 0;

  log(`🤖 Memulai Bulk Auto Token Bot (Figma) — ${accounts.length} akun...`, "start");

  if (!accounts || !accounts.length) {
    const err = "❌ Daftar akun kosong.";
    log(err, "error");
    return { ok: false, error: err, results: [], total: 0, success: 0, failed: 0 };
  }

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

    // Proses akun satu per satu (Figma sign-up/login + Weavy login)
    for (let i = 0; i < accounts.length; i++) {
      const acc = accounts[i];
      const email = (acc.email || "").trim();
      if (!email || !email.includes("@")) continue;

      const password = acc.password || generateFigmaPassword();
      const label = email.split("@")[0];

      log(`\n━━━ [${i + 1}/${accounts.length}] Memproses: ${email} ━━━`);

      const result = {
        email,
        label,
        ok: false,
        error: null,
        accountId: null,
        tokenPreview: null,
        recipeId: null,
      };

      let context = null;
      try {
        context = await createStealthContext(browser);

        if (acc.password) {
          // Akun Figma sudah ada → login ke Figma dulu
          log(`🔐 Login ke Figma: ${email}...`);
          const figmaPage = await context.newPage();
          await signInFigma(email, password, figmaPage, log);
          await figmaPage.close().catch(() => {});
        } else {
          // Buat akun Figma baru → signup
          log(`🆕 Mendaftar akun Figma: ${email}...`);
          await signUpFigma(email, password, context, log);
        }

        // Login ke Weavy via Figma (session Figma sudah aktif)
        log(`🔐 Login ke Weavy via Figma OAuth...`);
        const { token } = await signInWeavyViaFigma(context, log);

        // Ensure Kling 2.6 recipe
        log("📋 Memastikan recipe Kling 2.6...");
        const recipeId = await ensureKling26Recipe(token, null, log);

        // Simpan ke pool
        log(`💾 Menyimpan akun ke pool: ${email}...`);
        const savedAcc = Accounts.addAccount({ token, recipeId, label, email, password });

        result.ok = true;
        result.accountId = savedAcc.id;
        result.tokenPreview = token.substring(0, 25) + "...";
        result.recipeId = recipeId;

        log(`✅ BERHASIL: ${email} (${savedAcc.id})`, "success");
        success++;
      } catch (err) {
        log(`❌ GAGAL: ${email} — ${err.message}`, "error");
        result.error = err.message;
        failed++;
      } finally {
        if (context) {
          await context.close().catch(() => {});
        }
      }

      results.push(result);

      // Jeda antar akun
      if (i < accounts.length - 1) {
        log("⏳ Jeda 5 detik sebelum akun berikutnya...");
        await sleep(5000);
      }
    }

    await browser.close();
    activeBrowser = null;

    const summary = `Selesai: ${success} berhasil, ${failed} gagal dari ${accounts.length} akun.`;
    log(`\n${summary}`, success === accounts.length ? "success" : "warn");

    return {
      ok: true,
      results,
      total: accounts.length,
      success,
      failed,
      msg: summary,
    };
  } catch (err) {
    log(`❌ ERROR FATAL: ${err.message}`, "error");
    if (browser) await browser.close().catch(() => {});
    activeBrowser = null;
    return { ok: false, error: err.message, results, total: accounts.length, success, failed };
  }
}

// ─── REVIVE: Refresh token akun existing via Google ─────────────────

export async function refreshExistingAccountToken(accountId, email, _unusedPassword, onLog = null) {
  const log = makeLogger(onLog);
  let browser = null;

  const reviveStartedAt = new Date().toISOString();
  log(`🔄 [REVIVE] Memulai revive akun: ${email} (${accountId})`, "start");
  log(`⏱️ [REVIVE] Waktu mulai: ${reviveStartedAt}`);

  try {
    // Validasi: ambil password dari store — WAJIB ada, tidak boleh generate random
    const accounts = Accounts.listAccounts();
    const storedAcc = accounts.find(a => a.id === accountId || a.email === email);
    
    if (!storedAcc) {
      throw new Error(`Akun ${email} tidak ditemukan di account store.`);
    }
    
    const password = storedAcc.password || _unusedPassword || "asu12345";
    if (!password) {
      throw new Error(`Password tidak tersimpan untuk akun ${email}. Akun ini dibuat tanpa password Figma — tidak bisa di-revive.`);
    }
    
    log(`🔍 [REVIVE] Status akun: expired=${storedAcc.expired}, runs=${storedAcc.runs || 0}/${storedAcc.maxRuns || 3}, figmaBlocked=${storedAcc.figmaBlocked || storedAcc.googleBlocked || false}`);

    // Cek apakah akun sudah di-block (Figma / Google)
    if (storedAcc.figmaBlocked || storedAcc.googleBlocked) {
      const reason = storedAcc.figmaBlockedReason || storedAcc.googleBlockedReason || "tidak diketahui";
      throw new Error(`Akun ${email} sudah di-block (${reason}). Tidak bisa di-revive.`);
    }

    const hl = isHeadless();
    const playwright = await getPlaywright();
    browser = await playwright.chromium.launch(getLaunchOptions(hl));
    activeBrowser = browser;

    const context = await createStealthContext(browser);

    // Login ke Figma dengan email + password (pakai session existing)
    log(`🔐 [REVIVE] Login ke Figma: ${email}...`);
    const figmaPage = await context.newPage();
    await signInFigma(email, password, figmaPage, log);
    await figmaPage.close().catch(() => {});

    // Sign-in via Figma OAuth (session Figma sudah aktif)
    log(`🔐 [REVIVE] Login ke Weavy via Figma OAuth...`);
    const { token } = await signInWeavyViaFigma(context, log);

    // Ensure recipe
    const recipeId = await ensureKling26Recipe(token, null, log);

    // Update account — reset status expired & perpanjang masa aktif
    const updatedFields = {
      token,
      recipeId,
      expired: false,
      expiredAt: null,
      expireReason: null,
      figmaBlocked: false,
      googleBlocked: false,
      lastRevivedAt: new Date().toISOString(),
    };
    Accounts.updateAccount(accountId, updatedFields);
    
    log(`✅ [REVIVE] SUKSES: ${email}`, "success");
    log(`📋 [REVIVE] Token baru: ${token.substring(0, 25)}...`);
    log(`📋 [REVIVE] Recipe: ${recipeId}`);
    log(`⏱️ [REVIVE] Selesai: ${new Date().toISOString()}`);

    await browser.close();
    activeBrowser = null;

    return { ok: true, token, recipeId };
  } catch (err) {
    const failedAt = new Date().toISOString();
    log(`❌ [REVIVE] GAGAL: ${email} — ${err.message}`, "error");
    log(`⏱️ [REVIVE] Gagal pada: ${failedAt}`);

    // Tandai akun sebagai Figma-blocked jika login gagal karena kredensial / verifikasi
    const blockKeywords = [
      "memblokir", "blocked", "tidak bisa login", "password salah",
      "invalid credentials", "authentication failed", "kredensial tidak valid",
      "session invalid", "tidak ditemukan di account store", "sudah di-block",
    ];
    const shouldBlock = blockKeywords.some(kw => err.message.toLowerCase().includes(kw.toLowerCase()));

    if (shouldBlock) {
      try {
        await Accounts.updateAccount(accountId, {
          figmaBlocked: true,
          googleBlocked: true,
          figmaBlockedAt: failedAt,
          figmaBlockedReason: err.message,
          googleBlockedAt: failedAt,
          googleBlockedReason: err.message,
        });
        log(`🚫 [REVIVE] Akun ${email} ditandai FIGMA_BLOCKED — tidak akan dipilih lagi.`, "error");
      } catch (markErr) {
        log(`⚠️ [REVIVE] Gagal tandai figmaBlocked: ${markErr.message}`, "warn");
      }
    }

    if (browser) await browser.close().catch(() => {});
    activeBrowser = null;
    return { ok: false, error: err.message };
  }
}
