// ============================================================
//  VisaFlowX — CaptchaSolver  (safe handoff observer)
//  File: content/captcha-solver.js
//
//  STRATEGY OVERVIEW
//  -----------------
//  This module detects visible verification widgets, focuses/highlights
//  them for manual completion, observes response-token completion, and
//  reports state transitions back to the background controller.
//
//  It does not inject tokens, use debugger automation, synthesize trusted
//  challenge clicks, or manipulate cookies.
// ============================================================

(function initVisaFlowXCaptchaSolver(global) {
  "use strict";

  // Guard against double-init
  if (global.__VisaFlowXCaptchaSolverReady) return;
  global.__VisaFlowXCaptchaSolverReady = true;

  const LOG = "[VisaFlowX CaptchaSolver]";
  const VERIFICATION_LOG = "[VisaFlowX][Verification]";

  // ─────────────────────────────────────────────────────────────
  // Helpers & Selectors
  // ─────────────────────────────────────────────────────────────
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

  function logVerification(event, details = {}) {
    console.log(VERIFICATION_LOG, event, details);
  }

  function deepQuerySelector(selector, root = document) {
    const el = root.querySelector(selector);
    if (el) return el;
    const allElems = root.querySelectorAll('*');
    for (const elem of allElems) {
      if (elem.shadowRoot) {
        const found = deepQuerySelector(selector, elem.shadowRoot);
        if (found) return found;
      }
    }
    return null;
  }

  function deepQuerySelectorAll(selector, root = document, result = []) {
    root.querySelectorAll?.(selector).forEach((element) => result.push(element));
    const allElems = root.querySelectorAll ? root.querySelectorAll("*") : [];
    for (const elem of allElems) {
      if (elem.shadowRoot) deepQuerySelectorAll(selector, elem.shadowRoot, result);
    }
    return result;
  }

  async function waitForElement(selectors, timeoutMs = 10000, root = document) {
    const deadline = Date.now() + timeoutMs;
    const selectorList = Array.isArray(selectors) ? selectors : [selectors];
    while (Date.now() < deadline) {
      for (const selector of selectorList) {
        const el = deepQuerySelector(selector, root);
        if (el && el.getBoundingClientRect().width > 0) return el;
      }
      await sleep(50);
    }
    return null;
  }

  async function verificationPolicy() {
    try {
      if (!chrome?.runtime?.sendMessage) return { allowed: true, mode: "safe_handoff", settings: {} };
      const response = await chrome.runtime.sendMessage({ type: "CAN_RUN_VERIFICATION_AUTOMATION" }).catch(() => null);
      if (!response?.ok) return { allowed: true, mode: "safe_handoff", settings: {} };
      return {
        allowed: response.allowed === true,
        mode: response.mode || "safe_handoff",
        settings: response.settings || {}
      };
    } catch (error) {
      console.warn(LOG, "Failed to fetch verification policy:", error.message);
      return { allowed: true, mode: "safe_handoff", settings: {} };
    }
  }

  function canAssistVerification(policy) {
    return policy?.allowed === true && policy.mode === "safe_handoff";
  }

  const CF_CHECKBOX_SELECTORS = [
    ".ctp-checkbox-label",
    "#cf-stage .ctp-checkbox-label",
    ".ctp-checkbox-container label",
    'label[for*="turnstile"]',
    '[role="checkbox"]',
    "input[type='checkbox']",
    "#checkbox",
    ".Checkbox input",
    "#cf-stage",
    ".ctp-checkbox-container"
  ];

  const CF_SUCCESS_SELECTORS = [
    ".ctp-success",
    ".mark.mark-green",
    ".checkmark",
    ".ctp-checkbox-label.is-checked",
    ".is-success"
  ];

  const CF_CHALLENGE_SELECTORS = [
    ".challenge-form",
    "#challenge-form",
    ".ctp-challenge-container"
  ];

  const CF_WIDGET_SELECTORS = [
    "iframe[src*='challenges.cloudflare.com']",
    "iframe[src*='turnstile']",
    "iframe[title*='Widget containing']",
    "iframe[title*='Cloudflare security challenge']",
    ".cf-turnstile",
    "[data-cf-turnstile]",
    "[data-sitekey]"
  ];
  const CF_RESPONSE_SELECTORS = [
    "input[name='cf-turnstile-response']",
    "textarea[name='cf-turnstile-response']",
    "input[name='cf_turnstile_response']",
    "input[name='cf_challenge_response']",
    "textarea[name='cf_challenge_response']",
    "[name='cf-turnstile-response']",
    "[name='cf_challenge_response']"
  ];
  const FORBIDDEN_INTERACTION_SELECTOR = [
    "input[type='password']",
    "button[aria-label*='password' i]",
    "button[title*='password' i]",
    "button[class*='password' i]",
    "button[class*='eye' i]",
    "button[class*='toggle' i]",
    ".eye-icon",
    ".password-toggle",
    ".icon",
    "svg",
    "img"
  ].join(",");
  const FORBIDDEN_CONTROL_RE = /(^|\s|-|_)(eye|toggle|password-toggle|show-password|hide-password|icon)(\s|-|_|$)/i;
  const TOKEN_STABILIZATION_MS = 0;
  let tokenCandidateValue = "";
  let tokenCandidateSeenAt = 0;
  let lastLoggedTokenLength = 0;

  function classText(element) {
    const value = element?.className;
    return String(value?.baseVal || value || "");
  }

  function isForbiddenVerificationTarget(element) {
    if (!element) return true;

    // Explicitly ALLOW Cloudflare iframes to fix the interaction regression
    if (element.tagName?.toLowerCase() === "iframe" && isCloudflareFrame(element)) {
      return false;
    }

    const tagName = element.tagName?.toLowerCase();
    if (["svg", "img"].includes(tagName)) return true;
    if (element.matches?.("input[type='password']")) return true;
    if (element.closest?.(FORBIDDEN_INTERACTION_SELECTOR)) return true;
    const identity = [
      classText(element),
      element.id,
      element.getAttribute?.("aria-label"),
      element.getAttribute?.("title"),
      element.getAttribute?.("data-testid")
    ].filter(Boolean).join(" ");
    return FORBIDDEN_CONTROL_RE.test(identity);
  }

  function focusVerificationElement(element) {
    if (!element?.getBoundingClientRect) return false;
    try {
      element.scrollIntoView?.({ behavior: "auto", block: "center", inline: "center" });
      element.focus?.({ preventScroll: true });
      element.style.outline = "3px solid rgba(56, 189, 248, .9)";
      element.style.outlineOffset = "4px";
      return true;
    } catch (_) {
      return false;
    }
  }

  async function dispatchPointerClick(element) {
    if (!element?.getBoundingClientRect || element.isConnected === false) return false;
    try {
      element.scrollIntoView?.({ behavior: "auto", block: "center", inline: "center" });
      const rect = element.getBoundingClientRect();
      // Add a small randomized jitter for human-like interaction
      const jitterX = (Math.random() - 0.5) * (rect.width * 0.2);
      const jitterY = (Math.random() - 0.5) * (rect.height * 0.2);
      const x = rect.left + rect.width / 2 + jitterX;
      const y = rect.top + rect.height / 2 + jitterY;

      const eventInit = {
        view: window,
        bubbles: true,
        cancelable: true,
        pointerId: 1,
        width: 1,
        height: 1,
        pressure: 0.5,
        tiltX: 0,
        tiltY: 0,
        pointerType: "mouse",
        isPrimary: true,
        clientX: x,
        clientY: y,
        screenX: x,
        screenY: y,
        buttons: 1,
        detail: 1
      };

      const order = [
        ["pointerover", PointerEvent],
        ["pointerenter", PointerEvent],
        ["mousemove", MouseEvent],
        ["pointerdown", PointerEvent],
        ["mousedown", MouseEvent],
        ["pointerup", PointerEvent],
        ["mouseup", MouseEvent],
        ["click", MouseEvent]
      ];

      for (const [type, Cls] of order) {
        const event = new Cls(type, eventInit);
        element.dispatchEvent(event);
        if (type !== "click") await sleep(5);
      }
      return true;
    } catch (error) {
      console.warn(LOG, "Pointer click failed", error.message);
      return false;
    }
  }

  // ─────────────────────────────────────────────────────────────
  // Parent page checks
  // ─────────────────────────────────────────────────────────────
  function isAlreadySolvedParent() {
    return getTokenState().stable;
  }

  function findResponseTokenInput(root = document) {
    for (const selector of CF_RESPONSE_SELECTORS) {
      const input = root.querySelector?.(selector);
      if (input) return input;
    }
    const allElems = root.querySelectorAll ? root.querySelectorAll("*") : [];
    for (const elem of allElems) {
      if (!elem.shadowRoot) continue;
      const found = findResponseTokenInput(elem.shadowRoot);
      if (found) return found;
    }
    return null;
  }

  function getTokenState() {
    const value = String(findResponseTokenInput()?.value || "").trim();
    const now = Date.now();
    if (value.length <= 20) {
      tokenCandidateValue = "";
      tokenCandidateSeenAt = 0;
      lastLoggedTokenLength = 0;
      return { stable: false, pending: false, value: "" };
    }

    if (value !== tokenCandidateValue) {
      tokenCandidateValue = value;
      tokenCandidateSeenAt = now;
      return { stable: true, pending: false, value };
    }

    return {
      stable: now - tokenCandidateSeenAt >= TOKEN_STABILIZATION_MS,
      pending: now - tokenCandidateSeenAt < TOKEN_STABILIZATION_MS,
      value
    };
  }

  function styleAllowsDisplay(element) {
    let current = element;
    while (current && current.nodeType === Node.ELEMENT_NODE) {
      if (current.closest?.("template,[hidden]")) return false;
      if (current.getAttribute?.("aria-hidden") === "true") return false;
      const style = window.getComputedStyle ? window.getComputedStyle(current) : null;
      if (style && (
        style.visibility === "hidden"
        || style.display === "none"
        || Number(style.opacity || 1) === 0
      )) {
        return false;
      }
      current = current.parentElement;
    }
    return true;
  }

  function isVisibleChallengeElement(element) {
    if (!element?.getBoundingClientRect || element.isConnected === false) return false;
    const rect = element.getBoundingClientRect();
    if (!rect || rect.width < 20 || rect.height < 20) return false;
    const viewportWidth = window.innerWidth || document.documentElement?.clientWidth || 0;
    const viewportHeight = window.innerHeight || document.documentElement?.clientHeight || 0;
    if (viewportWidth && (rect.right <= 0 || rect.left >= viewportWidth)) return false;
    if (viewportHeight && (rect.bottom <= 0 || rect.top >= viewportHeight)) return false;
    if (!styleAllowsDisplay(element)) return false;
    const style = window.getComputedStyle ? window.getComputedStyle(element) : null;
    if (style?.pointerEvents === "none") return false;
    return true;
  }

  function isVisibleStateElement(element) {
    if (!element?.getBoundingClientRect || element.isConnected === false) return false;
    const rect = element.getBoundingClientRect();
    if (!rect || rect.width <= 0 || rect.height <= 0) return false;
    if (!styleAllowsDisplay(element)) return false;
    return true;
  }

  function visibleStateText(root = document) {
    const target = root?.body || root?.documentElement || root;
    if (!target) return "";
    const renderedText = target.innerText || target.textContent || "";
    const text = [
      renderedText,
      target.getAttribute?.("aria-label"),
      target.getAttribute?.("title")
    ].filter(Boolean).join(" ");
    return text.replace(/\s+/g, " ").trim();
  }

  function hasSuccessText(root = document) {
    return /\bsuccess(?:!|\b)/i.test(visibleStateText(root));
  }

  function hasVisibleSuccess(root = document) {
    if (hasSuccessText(root)) return true;
    if (hasVerifiedCheckbox(root)) return true;
    return CF_SUCCESS_SELECTORS.some((selector) => {
      const elements = deepQuerySelectorAll(selector, root);
      return elements.some(isVisibleStateElement);
    });
  }

  function hasVerifiedCheckbox(root = document) {
    return Boolean(
      deepQuerySelector("[role='checkbox'][aria-checked='true']", root)
      || deepQuerySelector("input[type='checkbox']:checked", root)
      || deepQuerySelector(".ctp-checkbox-label.is-checked,.ctp-checkbox-label[aria-checked='true']", root)
    );
  }

  function isCloudflareFrame(element) {
    if (element?.tagName?.toLowerCase() !== "iframe") return false;
    const src = String(element.getAttribute("src") || element.src || "").toLowerCase();
    return src.includes("challenges.cloudflare.com") || src.includes("turnstile");
  }

  function isAllowedTurnstileTarget(element) {
    if (!element || isForbiddenVerificationTarget(element) || !isVisibleChallengeElement(element)) return false;
    if (element.querySelector?.("input[type='password']")) return false;
    if (isCloudflareFrame(element)) return true;
    if (element.matches?.(".cf-turnstile,[data-cf-turnstile],[data-sitekey]")) return true;
    if (element.querySelector?.("iframe[src*='challenges.cloudflare.com'], iframe[src*='turnstile']")) return true;
    return CF_RESPONSE_SELECTORS.some((selector) => element.querySelector?.(selector));
  }

  function resolveTurnstileTarget(root = document) {
    const detectorTarget = global.VisaFlowX?.Detector?.resolveTurnstileTarget?.(root);
    if (detectorTarget?.element) return detectorTarget;

    for (const selector of CF_WIDGET_SELECTORS) {
      const element = deepQuerySelectorAll(selector, root).find(isAllowedTurnstileTarget);
      if (element) return { selector, element };
    }

    for (const selector of CF_RESPONSE_SELECTORS) {
      const token = deepQuerySelector(selector, root);
      const container = token?.closest?.(".cf-turnstile,[data-cf-turnstile],[data-sitekey],.cf-turnstile-wrapper");
      if (isAllowedTurnstileTarget(container)) return { selector: "turnstile-response-container", element: container };
    }

    if (isInsideCfIframe()) {
      for (const selector of CF_CHECKBOX_SELECTORS) {
        const element = deepQuerySelectorAll(selector, root).find(isVisibleChallengeElement);
        if (element) return { selector, element };
      }
    }

    return null;
  }

  function findVisibleWidget() {
    return resolveTurnstileTarget();
  }

  function detectChallengeState() {
    const tokenState = getTokenState();
    const successVisible = isInsideCfIframe() && hasVisibleSuccess();
    const checkboxVerified = isInsideCfIframe() && hasVerifiedCheckbox();
    if (tokenState.stable || successVisible || checkboxVerified) {
      return {
        active: false,
        completed: true,
        selector: tokenState.stable ? "cf-turnstile-response" : checkboxVerified ? "cf-checkbox-verified" : "cf-success",
        source: isInsideCfIframe() ? "cf_frame" : "page",
        tokenFound: Boolean(tokenState.value),
        tokenLength: tokenState.value?.length || 0,
        successVisible,
        checkboxVerified
      };
    }

    const widget = findVisibleWidget();
    if (widget) {
      return {
        active: true,
        completed: false,
        selector: widget.selector,
        source: isInsideCfIframe() ? "cf_frame" : "page"
      };
    }

    return {
      active: false,
      completed: false,
      pending: tokenState.pending,
      selector: tokenState.pending ? "cf-turnstile-response" : "",
      tokenFound: Boolean(tokenState.value),
      tokenLength: tokenState.value?.length || 0
    };
  }

  let challengeObserver = null;
  let challengeReportTimer = null;
  let challengePollTimer = null;
  let lastReportedChallengeSignature = "";
  let lastReportedActive = false;

  function challengeSignature(state) {
    return [
      state.active ? "active" : "inactive",
      state.completed ? "completed" : "pending",
      state.selector || "",
      location.href
    ].join("|");
  }

  function reportChallengeState() {
    if (!chrome?.runtime?.id) {
      stopChallengePolling();
      if (challengeObserver) {
        challengeObserver.disconnect();
        challengeObserver = null;
      }
      return;
    }
    const state = detectChallengeState();
    if (state.pending) scheduleChallengeReport(TOKEN_STABILIZATION_MS + 50);
    if (state.tokenFound && state.tokenLength !== lastLoggedTokenLength) {
      lastLoggedTokenLength = state.tokenLength;
      logVerification("token found", {
        stable: state.completed === true,
        length: state.tokenLength
      });
    }
    const signature = challengeSignature(state);
    const shouldReport = state.active || state.completed || lastReportedActive;
    if (!shouldReport || signature === lastReportedChallengeSignature) return;

    lastReportedChallengeSignature = signature;
    lastReportedActive = state.active;
    if (state.completed) {
      stopChallengePolling();
      if (state.successVisible) logVerification("success visible", { selector: state.selector || "" });
      if (state.checkboxVerified) logVerification("checkbox verified", { selector: state.selector || "" });
      logVerification("verification success detected", {
        selector: state.selector || "",
        source: state.source || (isInsideCfIframe() ? "cf_frame" : "page")
      });
    }
    logVerification("background message sent", {
      type: "CHALLENGE_STATE",
      active: state.active === true,
      completed: state.completed === true,
      selector: state.selector || ""
    });
    try {
      if (!chrome?.runtime?.id) {
        stopChallengePolling();
        if (challengeObserver) {
          challengeObserver.disconnect();
          challengeObserver = null;
        }
        return;
      }
      chrome.runtime.sendMessage({
        type: "CHALLENGE_STATE",
        active: state.active,
        completed: state.completed,
        selector: state.selector,
        source: state.source || (isInsideCfIframe() ? "cf_frame" : "page"),
        url: location.href
      }).catch(() => {});
    } catch (e) {
      console.warn(LOG, "Challenge report failed (context probably invalidated):", e.message);
      stopChallengePolling();
      if (challengeObserver) {
        challengeObserver.disconnect();
        challengeObserver = null;
      }
    }
  }

  function scheduleChallengeReport(delayMs = 25) {
    if (challengeReportTimer) clearTimeout(challengeReportTimer);
    challengeReportTimer = setTimeout(() => {
      challengeReportTimer = null;
      reportChallengeState();
    }, Math.max(0, Number(delayMs) || 0));
  }

  function stopChallengePolling() {
    if (challengePollTimer) clearInterval(challengePollTimer);
    challengePollTimer = null;
  }

  function startChallengePolling() {
    if (challengePollTimer || !isInsideCfIframe()) return;
    challengePollTimer = setInterval(reportChallengeState, 100);
  }

  function startChallengeStateObserver() {
    if (global.__VisaFlowXChallengeObserverAttached) return;
    if (!document.documentElement) return;
    global.__VisaFlowXChallengeObserverAttached = true;
    if (challengeObserver) return;
    reportChallengeState();
    challengeObserver = new MutationObserver(scheduleChallengeReport);
    challengeObserver.observe(document.documentElement, {
      childList: true,
      subtree: true,
      characterData: true,
      attributes: true,
      attributeFilter: ["class", "style", "src", "value", "aria-checked", "data-sitekey"]
    });
    startChallengePolling();
    window.addEventListener("pagehide", () => {
      if (challengeObserver) challengeObserver.disconnect();
      challengeObserver = null;
      if (challengeReportTimer) clearTimeout(challengeReportTimer);
      challengeReportTimer = null;
      stopChallengePolling();
    }, { once: true });
  }

  // ─────────────────────────────────────────────────────────────
  // STRATEGY A: Run INSIDE the CF iframe
  // ─────────────────────────────────────────────────────────────
  async function runInsideCfIframe() {
    const policy = await verificationPolicy();
    if (!canAssistVerification(policy)) return false;
    logVerification("iframe found", { url: location.href });

    const checkToken = () => {
      const state = getTokenState();
      return state.stable || state.pending;
    };
    const checkSolved = () => hasVisibleSuccess() || checkToken();
    const checkChallenge = () => CF_CHALLENGE_SELECTORS.some(s => !!deepQuerySelector(s));

    if (checkSolved()) {
      console.log(LOG, "Verification already completed, signaling background");
      logVerification("token detected", { source: "initial_frame_check" });
      logVerification("background message sent", { type: "CAPTCHA_RESULT", success: true });
      if (chrome?.runtime?.sendMessage) chrome.runtime.sendMessage({ type: "CAPTCHA_RESULT", success: true });
      return true;
    }

    console.log(LOG, "Safe verification handoff active inside frame");
    const checkbox = await waitForElement(CF_CHECKBOX_SELECTORS, 5000);
    if (!checkbox) {
      // Re-check success before giving up
      if (checkSolved()) {
        logVerification("token detected", { source: "late_frame_check" });
        logVerification("background message sent", { type: "CAPTCHA_RESULT", success: true });
        if (chrome?.runtime?.sendMessage) chrome.runtime.sendMessage({ type: "CAPTCHA_RESULT", success: true });
        return true;
      }
      return false;
    }

    // Strategy: Click the specific checkbox OR the entire stage as a fallback
    const stage = deepQuerySelector("#cf-stage") || deepQuerySelector(".ctp-checkbox-container");
    const target = checkbox || stage;
    
    if (!target) return false;

    console.log(LOG, "Attempting interaction on", target.tagName, target.className);
    await dispatchPointerClick(target);
    logVerification("checkbox clicked", {
      tagName: target.tagName || "",
      className: String(target.className || "")
    });

    // One more try on the checkbox specifically if we used the stage first
    if (checkbox && target !== checkbox) {
        await sleep(120);
        await dispatchPointerClick(checkbox);
    }

    let observerDone = false;
    let challengeReported = false;
    let observerCleanupTimer = null;
    const disconnectObserver = () => {
      if (observerDone) return;
      observerDone = true;
      observer.disconnect();
      if (observerCleanupTimer) clearTimeout(observerCleanupTimer);
      observerCleanupTimer = null;
    };

    const observer = new MutationObserver(() => {
      if (checkSolved()) {
        console.log(LOG, "Verification completed");
        disconnectObserver();
        stopChallengePolling();
        logVerification("token detected", { source: "frame_observer" });
        logVerification("background message sent", { type: "CAPTCHA_RESULT", success: true });
        if (chrome?.runtime?.sendMessage) chrome.runtime.sendMessage({ type: "CAPTCHA_RESULT", success: true });
      } else if (!challengeReported && checkChallenge()) {
        challengeReported = true;
        console.warn(LOG, "Verification challenge requires manual completion");
        if (chrome?.runtime?.sendMessage) chrome.runtime.sendMessage({ type: "CAPTCHA_RESULT", success: false, challenge: true });
      }
    });
    observer.observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true });
    observerCleanupTimer = setTimeout(disconnectObserver, 10000);

    return true;
  }

  // ─────────────────────────────────────────────────────────────
  // STRATEGY B: Hardware click remains disabled in safe handoff mode.
  // ─────────────────────────────────────────────────────────────
  async function strategyB_hardwareClick() {
    return false;
  }

  // ─────────────────────────────────────────────────────────────
  // STRATEGY C: Focus the parent page wrapper without synthetic clicks.
  // ─────────────────────────────────────────────────────────────
  function strategyC_parentWrapper() {
    const target = resolveTurnstileTarget();
    if (!target?.element || isInsideCfIframe()) return false;
    return focusVerificationElement(target.element);
  }

  function isInsideCfIframe() {
    const href = window.location.href;
    return href.includes("challenges.cloudflare.com") || !!document.querySelector(".ctp-checkbox-label");
  }

  async function pollForToken(timeoutMs = 8000) {
    const deadline = Date.now() + timeoutMs;
    while (Date.now() < deadline) {
      if (isAlreadySolvedParent()) return true;
      await sleep(50);
    }
    return false;
  }

  function isSolvingIframe() {
    return !!(
      document.querySelector(".ctp-spinner, .ctp-checking, #ctp-checking") ||
      document.querySelector(".ctp-checkbox-container input:disabled") ||
      document.querySelector(".ctp-checkbox-label.is-checking")
    );
  }

  // ─────────────────────────────────────────────────────────────
  // PUBLIC API
  // ─────────────────────────────────────────────────────────────
  async function tryAutoSolve(verificationState) {
    const policy = await verificationPolicy();
    if (!canAssistVerification(policy)) return { attempted: false, solved: false };

    const isCf =
      verificationState?.selector?.includes("challenges.cloudflare.com") ||
      verificationState?.selector?.includes("cf-turnstile") ||
      !!resolveTurnstileTarget();

    if (!isCf) return { attempted: false, solved: false };
    if (isAlreadySolvedParent()) return { attempted: true, solved: true };

    if (isSolvingIframe()) {
      console.log(LOG, "Verification in progress");
      const solved = await pollForToken(800);
      return { attempted: true, solved };
    }

    console.log(LOG, "Safe verification handoff active");
    strategyB_hardwareClick().catch(() => {});
    strategyC_parentWrapper();

    const solved = await pollForToken(isInsideCfIframe() ? 800 : 50);
    return { attempted: true, solved };
  }

  // ─────────────────────────────────────────────────────────────
  // AUTO-RUN
  // ─────────────────────────────────────────────────────────────
  if (chrome?.runtime?.onMessage) {
    chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
      if (message?.type !== "GET_CHALLENGE_STATE") return false;
      const state = detectChallengeState();
      sendResponse({
        ok: true,
        challenge: state,
        insideCfFrame: isInsideCfIframe(),
        url: location.href,
        hostname: location.hostname
      });
      return false;
    });
  }

  if (isInsideCfIframe()) {
    runInsideCfIframe().catch(() => {});
  }
  startChallengeStateObserver();

  const CaptchaSolver = Object.freeze({ detectChallengeState, isInsideCfIframe, resolveTurnstileTarget, tryAutoSolve });
  global.VisaFlowX = Object.assign(global.VisaFlowX || {}, { CaptchaSolver });

})(typeof globalThis !== "undefined" ? globalThis : this);
