import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
import readline from "readline";
import ollama from "ollama";
import fs from "fs";
import { exec, execSync } from "child_process";
import { platform } from "os";

puppeteer.use(StealthPlugin());

// ─────────────────────────────────────────────
//  PLATFORM DETECTION
// ─────────────────────────────────────────────
const OS = platform(); // 'darwin' | 'win32' | 'linux'

function getChromePath() {
  if (OS === "win32") {
    const paths = [
      "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
      "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
      process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe",
    ];
    for (const p of paths) {
      if (fs.existsSync(p)) return p;
    }
    throw new Error("Chrome not found on Windows. Install Google Chrome first.");
  }
  if (OS === "darwin") {
    return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
  }
  // Linux
  for (const p of ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium"]) {
    if (fs.existsSync(p)) return p;
  }
  throw new Error("Chrome/Chromium not found on Linux. Install with: sudo apt install chromium-browser");
}

// ─────────────────────────────────────────────
//  TEXT-TO-SPEECH (cross-platform)
// ─────────────────────────────────────────────
function speak(text) {
  const safe = text.replace(/"/g, "");
  try {
    if (OS === "darwin")  exec(`say "${safe}"`);
    else if (OS === "win32") exec(`powershell -Command "Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('${safe}')"`);
    else exec(`espeak "${safe}" 2>/dev/null || true`);
  } catch (_) { /* TTS is optional — never crash */ }
}

// ─────────────────────────────────────────────
//  VOICE INPUT (cross-platform via Python)
// ─────────────────────────────────────────────
async function listenVoice() {
  const { promisify } = await import("util");
  const execP = promisify(exec);
  try {
    const { stdout } = await execP("python3 listen.py", { timeout: 12000 });
    const match = stdout.match(/TEXT:\s*(.*)/);
    if (match && match[1] && !match[1].includes("COULD_NOT_UNDERSTAND") && !match[1].includes("ERROR")) {
      return match[1].trim();
    }
  } catch (_) {}
  return null;
}

// ─────────────────────────────────────────────
//  INPUT: VOICE → TEXT FALLBACK
// ─────────────────────────────────────────────
function askQuestion(query) {
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
  return new Promise((resolve) => rl.question(query, (ans) => { rl.close(); resolve(ans.trim()); }));
}

async function ask(query) {
  speak(query);
  console.log(`\n🗣️  ${query}`);
  console.log("🎤 Listening for 5 seconds... (or just type your answer below)");
  const voice = await listenVoice();
  if (voice) {
    console.log(`   You said: "${voice}"`);
    return voice;
  }
  console.log("   ⚠️  Voice unavailable — please type:");
  return await askQuestion("   > ");
}

// ─────────────────────────────────────────────
//  UTILITIES
// ─────────────────────────────────────────────
function generatePassword() {
  const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
  let pwd = "";
  for (let i = 0; i < 12; i++) pwd += chars.charAt(Math.floor(Math.random() * chars.length));
  return pwd + "aA1!";
}

function parseDOB(dobStr) {
  if (!dobStr) return {};
  const parts = dobStr.split(/[/-]/);
  const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  if (parts.length === 3) {
    const [d, m, y] = parts;
    const mIdx = parseInt(m, 10) - 1;
    return {
      dob_day: d.padStart(2, '0'),
      dob_day_num: parseInt(d, 10),
      dob_month: m.padStart(2, '0'),
      dob_month_num: parseInt(m, 10),
      dob_month_name: monthNames[mIdx] || "",
      dob_year: y
    };
  }
  return {};
}

function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }

// ─────────────────────────────────────────────
//  HUMAN-LIKE BROWSER ACTIONS
// ─────────────────────────────────────────────
async function humanMove(page, selector) {
  try {
    const el = await page.$(selector);
    if (!el) return;
    const box = await el.boundingBox();
    if (!box) return;
    const x = box.x + box.width / 2 + (Math.random() * 10 - 5);
    const y = box.y + box.height / 2 + (Math.random() * 10 - 5);
    await page.mouse.move(x, y, { steps: 25 });
    await sleep(200 + Math.random() * 300);
    await page.mouse.click(x, y);
  } catch (_) {}
}

async function humanType(page, selector, text) {
  try {
    const el = await page.$(selector);
    if (!el) { console.log(`   ⚠️  Element not found: ${selector}`); return; }
    await page.focus(selector);
    await sleep(200);

    // Trigger React/Vue/Angular native setters
    await page.$eval(selector, (el, value) => {
      const proto = el.tagName.toLowerCase() === "textarea"
        ? window.HTMLTextAreaElement.prototype
        : window.HTMLInputElement.prototype;
      const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
      if (setter) {
        setter.call(el, value);
        el.dispatchEvent(new Event("input",  { bubbles: true }));
        el.dispatchEvent(new Event("change", { bubbles: true }));
      } else {
        el.value = value;
      }
    }, String(text));

    await sleep(150);

    // Also type char-by-char so keydown/keyup handlers fire
    await page.click(selector, { clickCount: 3 });
    await page.keyboard.press("Backspace");
    for (const char of String(text)) {
      await page.type(selector, char, { delay: 60 + Math.random() * 80 });
    }
  } catch (e) {
    console.log(`   ⚠️  humanType error on ${selector}:`, e.message);
  }
}

// ─────────────────────────────────────────────
//  DOM EXTRACTION
// ─────────────────────────────────────────────
async function extractInteractiveElements(page) {
  return await page.evaluate(() => {
    const elements = [];
    let idCounter = 1;

    document.querySelectorAll('input, select, textarea, [role="listbox"], [role="combobox"]').forEach((el) => {
      const rect = el.getBoundingClientRect();
      if (rect.width > 0 && rect.height > 0) {
        if (!el.id) el.id = `llm-id-${idCounter++}`;
        const data = {
          tag: el.tagName.toLowerCase(),
          type: el.type || "",
          name: el.name || "",
          id: el.id,
          placeholder: el.placeholder || "",
          ariaLabel: el.getAttribute("aria-label") || "",
          currentValue: el.value || el.innerText || "",
        };
        if (el.tagName.toLowerCase() === "select") {
          data.options = Array.from(el.options).map((o) => ({ value: o.value, text: o.text }));
        }
        elements.push(data);
      }
    });

    document.querySelectorAll('button, a, [role="button"]').forEach((el) => {
      const rect = el.getBoundingClientRect();
      if (rect.width > 0 && rect.height > 0 && el.innerText.trim() !== "") {
        if (!el.id) el.id = `llm-id-${idCounter++}`;
        elements.push({ tag: el.tagName.toLowerCase(), text: el.innerText.trim(), href: el.href || "", id: el.id });
      }
    });

    return elements;
  });
}

async function extractPageErrors(page) {
  return await page.evaluate(() => {
    const errs = [];
    document.querySelectorAll("div, span, p, label, small").forEach((el) => {
      const t = el.innerText.toLowerCase();
      const keywords = ["taken", "invalid", "error", "already in use", "incorrect", "must contain", "try another", "too short", "already exists"];
      if (keywords.some((k) => t.includes(k))) {
        const style = window.getComputedStyle(el);
        if (style.display !== "none" && style.visibility !== "hidden" && el.getBoundingClientRect().height > 0) {
          errs.push(el.innerText.trim());
        }
      }
    });
    return [...new Set(errs)];
  });
}

// ─────────────────────────────────────────────
//  AI DECISION (Ollama Llama 3.2)
// ─────────────────────────────────────────────
async function askLlama(elements, profile, actionHistory, pageErrors, userFeedback) {
  const prompt = `You are an autonomous web automation agent. Your goal is to register a new user on the current website.

USER PROFILE (use ONLY these values when filling forms):
${JSON.stringify(profile, null, 2)}

USER FEEDBACK FROM LAST ATTEMPT (if any):
"${userFeedback || "None"}"

INTERACTIVE ELEMENTS ON CURRENT SCREEN:
${JSON.stringify(elements, null, 2)}

PREVIOUS ACTIONS TAKEN:
${JSON.stringify(actionHistory, null, 2)}

VISIBLE PAGE ERRORS (fix these if they block registration!):
${JSON.stringify(pageErrors, null, 2)}

RULES:
1. If a field's "currentValue" is already correctly filled — SKIP it (do not re-fill it).
2. Map fields strictly: email→email, username→username, password→password, display name/full name→firstName+lastName combined, DOB split→dob_day, dob_month/dob_month_name, dob_year.
3. If you see a landing/login page, output a "click" action on the Register/Sign Up button.
4. NEVER click "Login" or "Already have an account" links if on a registration page.
5. Use EXACT id strings from the elements list. NEVER invent ids.
6. Set "status" to "success" ONLY when you see a welcome/dashboard page confirming success.

Respond ONLY with valid JSON — no extra text, no markdown:
{
  "thought": "brief reasoning",
  "status": "success" | "progress" | "failed",
  "actions": [
    { "action": "click",  "elementId": "exact-id" },
    { "action": "type",   "elementId": "exact-id", "value": "text" },
    { "action": "select", "elementId": "exact-id", "value": "option value" }
  ]
}`;

  try {
    const response = await ollama.chat({
      model: "llama3.2",
      messages: [{ role: "user", content: prompt }],
      format: "json",
      options: { temperature: 0.1 },
    });
    return JSON.parse(response.message.content);
  } catch (err) {
    console.error("   Ollama error:", err.message);
    return { _error: true, actions: [] };
  }
}

// ─────────────────────────────────────────────
//  HEURISTIC FALLBACK (no Ollama needed)
// ─────────────────────────────────────────────
function heuristicFallback(elements, profile) {
  const actions = [];
  let clickedButton = false;

  for (const el of elements) {
    if (!el.id) continue;

    if ((el.tag === "button" || el.tag === "a") && !clickedButton) {
      const text = (el.text || "").toLowerCase();
      if (["register", "sign up", "create account", "submit", "continue", "get started"].some((k) => text.includes(k))) {
        actions.push({ action: "click", elementId: el.id });
        clickedButton = true;
        continue;
      }
    }

    if (el.tag === "input" || el.tag === "textarea") {
      const idStr = [el.name, el.id, el.placeholder, el.ariaLabel].join(" ").toLowerCase();
      const realName = `${profile.firstName} ${profile.lastName}`.trim();

      if ((idStr.includes("email") || idStr.includes("e-mail")) && el.type !== "password")
        actions.push({ action: "type", elementId: el.id, value: profile.email });
      else if (idStr.includes("pass") || idStr.includes("pwd"))
        actions.push({ action: "type", elementId: el.id, value: profile.password });
      else if (idStr.includes("first") || idStr.includes("fname"))
        actions.push({ action: "type", elementId: el.id, value: profile.firstName });
      else if (idStr.includes("last") || idStr.includes("lname"))
        actions.push({ action: "type", elementId: el.id, value: profile.lastName });
      else if (idStr.includes("display") || idStr.includes("full name") || idStr.includes("fullname") || (idStr.includes("name") && !idStr.includes("user") && !idStr.includes("pass") && !idStr.includes("email")))
        actions.push({ action: "type", elementId: el.id, value: realName });
      else if (idStr.includes("user") || idStr.includes("username"))
        actions.push({ action: "type", elementId: el.id, value: profile.username });
      else if (idStr.includes("dob") || idStr.includes("birth") || idStr.includes("date"))
        actions.push({ action: "type", elementId: el.id, value: profile.dob });
      else if (idStr.includes("day"))
        actions.push({ action: "type", elementId: el.id, value: profile.dob_day });
      else if (idStr.includes("month"))
        actions.push({ action: "type", elementId: el.id, value: profile.dob_month });
      else if (idStr.includes("year"))
        actions.push({ action: "type", elementId: el.id, value: profile.dob_year });
    }

    if (el.tag === "select") {
      const id = [el.name, el.id, el.ariaLabel].join(" ").toLowerCase();
      if (id.includes("country"))
        actions.push({ action: "select", elementId: el.id, value: profile.country });
      else if (id.includes("day")) {
        const match = el.options.find(o => o.value == profile.dob_day || o.text == profile.dob_day_num);
        if (match) actions.push({ action: "select", elementId: el.id, value: match.value });
      } else if (id.includes("month")) {
        const match = el.options.find(o => 
          o.value == profile.dob_month || 
          o.text.toLowerCase().includes(profile.dob_month_name.toLowerCase()) ||
          o.value == profile.dob_month_num
        );
        if (match) actions.push({ action: "select", elementId: el.id, value: match.value });
      } else if (id.includes("year")) {
        const match = el.options.find(o => o.value == profile.dob_year || o.text == profile.dob_year);
        if (match) actions.push({ action: "select", elementId: el.id, value: match.value });
      } else if ((id.includes("gender") || id.includes("sex")) && el.options) {
        const match = el.options.find((o) =>
          o.text.toLowerCase().includes(profile.gender?.toLowerCase()) ||
          o.value.toLowerCase().includes(profile.gender?.toLowerCase())
        );
        if (match) actions.push({ action: "select", elementId: el.id, value: match.value });
      }
    }
  }

  return {
    status: "progress",
    thought: actions.length > 0
      ? "Ollama unavailable. Used heuristic field matching."
      : "Ollama unavailable. No matching fields found by heuristics.",
    actions,
  };
}

// ─────────────────────────────────────────────
//  TEMP EMAIL GENERATION
// ─────────────────────────────────────────────
async function getTempEmail(page) {
  const providers = [
    {
      name: "1secmail (API)",
      isApi: true,
      fn: async () => {
        const res = await fetch("https://www.1secmail.com/api/v1/?action=genEmail&count=1");
        const data = await res.json();
        return data?.[0] || null;
      },
    },
    {
      name: "Temp-Mail.org",
      url: "https://temp-mail.org/en/",
      selector: "#mail",
      fn: async () => {
        await page.goto("https://temp-mail.org/en/", { waitUntil: "networkidle2", timeout: 20000 });
        await page.waitForSelector("#mail", { timeout: 15000 });
        return await page.$eval("#mail", (el) => (el.value || el.innerText || "").trim());
      },
    },
  ];

  for (const p of providers) {
    console.log(`📡 Trying provider: ${p.name}...`);
    try {
      const email = await p.fn();
      if (email && email.includes("@")) {
        console.log(`✅ Got temp email: ${email}`);
        return email;
      }
    } catch (err) {
      console.log(`   ❌ ${p.name} failed: ${err.message}`);
    }
  }
  return null;
}

// ─────────────────────────────────────────────
//  PROFILE COLLECTION
// ─────────────────────────────────────────────
const PROFILE_FILE = "./user_profiles.json";
const ACCOUNTS_FILE = "./accounts_db.json";

async function collectProfile() {
  if (fs.existsSync(PROFILE_FILE)) {
    try {
      const saved = JSON.parse(fs.readFileSync(PROFILE_FILE, "utf8"));
      const name = saved.username || saved.firstName || "unknown";
      const useSaved = await ask(`Found saved profile for "${name}". Use it? (yes/no)`);
      if (useSaved.toLowerCase().startsWith("y")) {
        console.log("   ✅ Using saved profile.");
        return saved;
      }
    } catch (_) {}
  }

  console.log("\n📋 Let's set up your profile...");
  const profile = {};
  profile.firstName = await ask("What is your first name?");
  profile.lastName  = await ask("What is your last name?");
  profile.username  = await ask("What is your preferred username?");
  profile.dob       = await ask("What is your date of birth? (DD/MM/YYYY)");
  profile.country   = await ask("What is your country?");
  profile.gender    = await ask("What is your gender?");

  const customPass  = await ask("Enter a master password, or say 'auto' to generate one:");
  profile.masterPassword = customPass.toLowerCase().includes("auto") ? null : customPass;

  fs.writeFileSync(PROFILE_FILE, JSON.stringify(profile, null, 2));
  console.log("💾 Profile saved to user_profiles.json");
  return profile;
}

// ─────────────────────────────────────────────
//  MAIN
// ─────────────────────────────────────────────
async function main() {
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
  console.log("        🤖 AI Registration Agent v2.0          ");
  console.log(`        Running on: ${OS}                      `);
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

  // 1. Collect profile
  const profile = await collectProfile();
  Object.assign(profile, parseDOB(profile.dob));

  // 2. Get target website
  let website = await ask("What is the target website URL?");
  if (!website.startsWith("http://") && !website.startsWith("https://")) {
    website = "https://" + website;
  }

  // 3. Launch browser
  let chromePath;
  try {
    chromePath = getChromePath();
  } catch (err) {
    console.error("❌", err.message);
    process.exit(1);
  }

  console.log(`\n🚀 Launching Chrome at: ${chromePath}`);
  const browser = await puppeteer.launch({
    headless: false,
    executablePath: chromePath,
    args: [
      "--no-sandbox",
      "--disable-blink-features=AutomationControlled",
      "--disable-infobars",
      "--window-size=1366,768",
    ],
    defaultViewport: null,
  });

  // 4. Setup stealth page
  const [page] = await browser.pages(); // reuse the default tab (fixes the duplicate `const page` bug)

  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, "webdriver",  { get: () => undefined });
    Object.defineProperty(navigator, "plugins",    { get: () => [1, 2, 3] });
    Object.defineProperty(navigator, "languages",  { get: () => ["en-US", "en"] });
  });
  await page.setUserAgent(
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
  );

  // 5. Get temp email
  console.log("\n📧 Generating temp email...");
  profile.email = await getTempEmail(page);
  if (!profile.email) {
    console.error("⛔ All temp-mail providers failed. Check your internet connection.");
    await browser.close();
    process.exit(1);
  }

  profile.password = profile.masterPassword || generatePassword();
  console.log(`🔑 Password set: ${profile.password}`);

  // 6. Navigate to site
  console.log(`\n🌐 Navigating to ${website}...`);
  const targetPage = await browser.newPage();
  await targetPage.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, "webdriver", { get: () => undefined });
  });
  await targetPage.setUserAgent(
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
  );

  // Try direct registration paths first
  let landed = false;
  for (const path of ["/register", "/signup", "/sign-up", "/join"]) {
    const testUrl = new URL(path, website).href;
    try {
      const res = await targetPage.goto(testUrl, { waitUntil: "networkidle2", timeout: 15000 });
      if (res && res.status() !== 404) {
        const cur = targetPage.url();
        if (!cur.includes("login") && !cur.includes("signin")) {
          console.log(`🎯 Found registration page at: ${cur}`);
          landed = true;
          break;
        }
      }
    } catch (_) {}
  }

  if (!landed) {
    console.log("🚦 Falling back to home page...");
    await targetPage.goto(website, { waitUntil: "networkidle2", timeout: 20000 });
  }

  await sleep(2000);

  // 7. AI automation loop
  const MAX_STEPS = 15;
  const actionHistory = [];
  let lastUserFeedback = null;

  for (let step = 1; step <= MAX_STEPS; step++) {
    console.log(`\n🤖 ── Step ${step}/${MAX_STEPS} ──`);

    let elements = [], pageErrors = [];
    try {
      elements   = await extractInteractiveElements(targetPage);
      pageErrors = await extractPageErrors(targetPage);
    } catch (_) {
      console.log("   Page is navigating — waiting 2s...");
      await sleep(2000);
      continue;
    }

    if (pageErrors.length > 0) console.log("   ⚠️  Page errors:", pageErrors);

    console.log("   🧠 Asking Llama 3.2...");
    let decision = await askLlama(elements, profile, actionHistory, pageErrors, lastUserFeedback);

    if (decision._error) {
      console.log("   ⚠️  Ollama offline — using heuristic fallback...");
      decision = heuristicFallback(elements, profile);
    }

    console.log(`   💭 AI: ${decision.thought || "—"}`);
    console.log(`   📊 Status: ${decision.status}`);

    if (decision.status === "success") {
      console.log("\n🎉 Registration complete!");

      // Save to accounts log
      let ledger = [];
      if (fs.existsSync(ACCOUNTS_FILE)) {
        try { ledger = JSON.parse(fs.readFileSync(ACCOUNTS_FILE, "utf8")); } catch (_) {}
      }
      ledger.push({ website, username: profile.username, email: profile.email, password: profile.password, savedAt: new Date().toISOString() });
      fs.writeFileSync(ACCOUNTS_FILE, JSON.stringify(ledger, null, 2));
      console.log(`💾 Credentials saved to accounts_db.json`);
      break;
    }

    if (!decision.actions?.length) {
      console.log("   🤷  No actions returned — stopping loop.");
      break;
    }

    // Sort: type/select first, click last (avoids mid-loop navigation crashes)
    const sorted = [...decision.actions].sort((a, b) => {
      if (a.action === "click" && b.action !== "click") return 1;
      if (a.action !== "click" && b.action === "click") return -1;
      return 0;
    });

    let successes = 0;
    for (const action of sorted) {
      if (!action.elementId) continue;
      actionHistory.push(action);

      const rawId = action.elementId.startsWith("#") ? action.elementId.slice(1) : action.elementId;
      let selector = `[id="${rawId.replace(/"/g, '\\"')}"]`;

      // Fallback to name attribute if id not found
      const found = await targetPage.$(selector).catch(() => null);
      if (!found) {
        const byName = await targetPage.$(`[name="${rawId}"]`).catch(() => null);
        if (byName) selector = `[name="${rawId}"]`;
      }

      try {
        if (action.action === "type") {
          console.log(`   → Typing "${action.value}" into ${selector}`);
          await humanMove(targetPage, selector);
          await humanType(targetPage, selector, action.value);
          successes++;
        } else if (action.action === "click") {
          console.log(`   → Clicking ${selector}`);
          await humanMove(targetPage, selector);
          await targetPage.click(selector);
          await sleep(2000);
          successes++;
        } else if (action.action === "select") {
          console.log(`   → Selecting "${action.value}" in ${selector}`);
          await targetPage.select(selector, action.value);
          await sleep(500);
          successes++;
        }
      } catch (err) {
        console.error(`   ❌ Action failed on ${selector}:`, err.message);
      }
    }

    // Ask user to confirm before submitting
    if (successes > 0) {
      const confirm = await ask("Does the form look correct? Say yes to submit, no to let AI retry.");
      const lower = confirm.toLowerCase();
      const positive = ["yes", "yup", "yeah", "submit", "ok", "sure", "haan", "done", "y"].some((w) => lower.includes(w));

      if (positive) {
        console.log("✅ Submitting...");
        await targetPage.evaluate(() => {
          const btns = Array.from(document.querySelectorAll("button"));
          const sub = btns.find((b) =>
            ["register", "submit", "continue", "create", "sign up"].some((k) => b.innerText.toLowerCase().includes(k))
          );
          if (sub) sub.click();
        });
        await sleep(3000);
        break;
      } else {
        lastUserFeedback = await ask("What exactly is wrong? (e.g. 'username is taken')");
        const lowerFeedback = lastUserFeedback.toLowerCase();
        
        if (lowerFeedback.includes("username")) {
          const old = profile.username;
          profile.username = profile.username.replace(/\d+$/, "") + Math.floor(100 + Math.random() * 899);
          console.log(`   🆕 Tweaked username: ${old} → ${profile.username}`);
          speak(`Okay, I have tweaked the username to ${profile.username}. Retrying.`);
        } else if (lowerFeedback.includes("password")) {
          profile.password = generatePassword();
          console.log(`   🔑 Regenerated password: ${profile.password}`);
          speak("Okay, I have generated a new password. Retrying.");
        } else {
          console.log("   🔄 Retrying with focus on:", lastUserFeedback);
          speak("Okay, I will try to fix that.");
        }
      }
    }

    await sleep(3000);
  }

  console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
  console.log("✅ Automation cycle complete.");
  console.log("   Browser left open for manual inspection.");
  console.log("   Press Ctrl+C to exit when done.");
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

  await new Promise(() => {}); // keep process alive
}

main().catch((err) => {
  console.error("💥 Fatal error:", err.message);
  process.exit(1);
});
