"""Approve xAI device-code in Chromium (DrissionPage).

Paths resolve relative to the project root.
Token polling is the source of truth; browser automation is only used to
log in and approve the device authorization page.
"""

from __future__ import annotations

import os
import re
import sys
import threading
import time
from pathlib import Path
from typing import Any, Callable, Optional
from urllib.parse import urlparse


LogFn = Callable[[str], None]


def _noop_log(_: str) -> None:
    return None


class BrowserConfirmError(RuntimeError):
    pass


def _sleep(sec: float) -> None:
    time.sleep(sec)


def _project_root() -> Path:
    return Path(__file__).resolve().parents[1]


def _debug_shot_dir() -> Path:
    folder = _project_root() / "screenshots"
    folder.mkdir(parents=True, exist_ok=True)
    return folder


def _safe_tag(value: str) -> str:
    text = (value or "na").strip()
    out = []
    for char in text:
        if char.isalnum() or char in ("@", ".", "-", "_"):
            out.append(char)
        else:
            out.append("_")
    return ("".join(out)[:80] or "na")


def _save_debug_shot(page: Any, tag: str, email: str = "", log: Optional[LogFn] = None) -> Optional[str]:
    logger = log or _noop_log
    try:
        timestamp = time.strftime("%Y%m%d-%H%M%S")
        name = "%s_%s_%s.png" % (timestamp, _safe_tag(email), _safe_tag(tag))
        path = _debug_shot_dir() / name
        saved = None
        for kwargs in (
            {"path": str(path), "full_page": True},
            {"path": str(path)},
            {"name": str(path)},
        ):
            try:
                if hasattr(page, "get_screenshot"):
                    page.get_screenshot(**kwargs)
                    saved = path
                    break
            except TypeError:
                continue
            except Exception:
                continue
        if saved is None and hasattr(page, "get_screenshot"):
            try:
                page.get_screenshot(path=str(path))
                saved = path
            except Exception:
                pass
        if saved is None:
            logger("screenshot failed tag=%s" % tag)
            return None
        try:
            meta = path.with_suffix(".txt")
            meta.write_text(
                "url=%s\nemail=%s\ntag=%s\nvisible=%s\n" % (_page_url(page), email, tag, _norm(_visible_text(page))[:800]),
                encoding="utf-8",
            )
        except Exception:
            pass
        logger("debug shot saved: %s" % saved)
        return str(saved)
    except Exception as exc:  # noqa: BLE001
        logger("screenshot error: %s" % exc)
        return None


def _is_turnstile_challenge(text: str) -> bool:
    source = text or ""
    lower = source.lower()
    needles = (
        "确认您是真人",
        "确认你是真人",
        "verify you are human",
        "confirm you are human",
        "just a moment",
        "checking your browser",
        "cf-turnstile",
        "进行人机验证",
        "人机验证",
    )
    return any(needle in source or needle in lower for needle in needles)


def create_standalone_page(proxy: Optional[str] = None, headless: bool = False, log: Optional[LogFn] = None):
    logger = log or _noop_log
    try:
        from DrissionPage import Chromium, ChromiumOptions
    except ImportError as exc:
        raise BrowserConfirmError("DrissionPage not installed") from exc

    options = None
    package_root = Path(__file__).resolve().parents[1]
    try:
        register_file = package_root / "grok_register_ttk.py"
        if register_file.is_file():
            register_dir = str(package_root)
            if register_dir not in sys.path:
                sys.path.insert(0, register_dir)
            try:
                from grok_register_ttk import create_browser_options  # type: ignore

                options = create_browser_options()
                logger("using register create_browser_options (turnstilePatch)")
            except Exception as exc:  # noqa: BLE001
                logger("register browser options unavailable: %s" % exc)
                options = None
    except Exception as exc:  # noqa: BLE001
        logger("register options probe failed: %s" % exc)
        options = None

    if options is None:
        options = ChromiumOptions()
        options.auto_port()
        options.set_timeouts(base=2)
        for flag in (
            "--disable-gpu",
            "--no-sandbox",
            "--disable-dev-shm-usage",
            "--mute-audio",
            "--no-first-run",
            "--disable-background-networking",
            "--window-size=1280,900",
        ):
            options.set_argument(flag)
        extension = str(package_root / "turnstilePatch")
        if os.path.isdir(extension):
            try:
                options.add_extension(extension)
                logger("added extension %s" % extension)
            except Exception as exc:  # noqa: BLE001
                logger("extension add failed: %s" % exc)

    if headless:
        try:
            options.headless(True)
        except Exception:
            options.set_argument("--headless=new")
        logger("headless=True (may hit Cloudflare / break real clicks)")
    else:
        try:
            options.headless(False)
        except Exception:
            pass

    for candidate in (
        "/usr/bin/chromium",
        "/usr/bin/chromium-browser",
        "/usr/bin/google-chrome",
        "/usr/bin/google-chrome-stable",
    ):
        if os.path.isfile(candidate):
            try:
                options.set_browser_path(candidate)
            except Exception:
                pass
            break

    from .proxyutil import proxy_for_chromium, proxy_log_label, resolve_proxy

    resolved = resolve_proxy(proxy)
    chrome_proxy = proxy_for_chromium(resolved)
    if chrome_proxy:
        options.set_argument("--proxy-server=%s" % chrome_proxy)
        logger("browser proxy=%s (chromium %s)" % (proxy_log_label(resolved), chrome_proxy))
    else:
        logger("browser proxy=(none)")

    browser = Chromium(options)
    page = browser.latest_tab
    logger("standalone chromium started")
    return browser, page


def close_standalone(browser: Any) -> None:
    try:
        browser.quit()
    except Exception:
        pass


_mint_tls = threading.local()


def _mint_tls_get():
    state = getattr(_mint_tls, "state", None)
    if state is None:
        state = {"browser": None, "page": None, "served": 0, "proxy": None, "headless": None}
        _mint_tls.state = state
    return state


def clear_page_session(page: Any, browser: Optional[Any] = None, log: Optional[LogFn] = None) -> None:
    logger = log or _noop_log
    try:
        if page is not None:
            try:
                page.get("about:blank")
            except Exception:
                pass
            for javascript in (
                "try{localStorage.clear()}catch(e){}",
                "try{sessionStorage.clear()}catch(e){}",
            ):
                try:
                    page.run_js(javascript)
                except Exception:
                    pass
        for target in (page, browser):
            if target is None:
                continue
            try:
                target.set.cookies.clear()  # type: ignore[attr-defined]
                logger("mint session cookies cleared")
                break
            except Exception:
                try:
                    cookies = target.cookies()
                    if isinstance(cookies, list):
                        for cookie in cookies:
                            try:
                                target.set.cookies.remove(cookie)  # type: ignore[attr-defined]
                            except Exception:
                                pass
                except Exception:
                    pass
    except Exception as exc:
        logger("clear_page_session: %s" % exc)


def normalize_cookies(cookies: Any):
    output = []
    if not cookies:
        return output
    if isinstance(cookies, dict):
        for name, value in cookies.items():
            if name and value is not None:
                output.append({"name": str(name), "value": str(value), "domain": ".x.ai", "path": "/"})
        cookies = output
        output = []
    if not isinstance(cookies, (list, tuple)):
        return output
    for cookie in cookies:
        if not isinstance(cookie, dict):
            continue
        name = cookie.get("name") or cookie.get("Name")
        value = cookie.get("value") or cookie.get("Value")
        if not name or value is None:
            continue
        domain = str(cookie.get("domain") or cookie.get("Domain") or ".x.ai")
        path = str(cookie.get("path") or cookie.get("Path") or "/")
        item = {"name": str(name), "value": str(value), "domain": domain, "path": path}
        for source, target in (
            ("expiry", "expiry"),
            ("expires", "expiry"),
            ("secure", "secure"),
            ("httpOnly", "httpOnly"),
            ("sameSite", "sameSite"),
        ):
            if source in cookie and cookie[source] is not None:
                item[target] = cookie[source]
        output.append(item)
    sso_names = {"sso", "sso-rw", "cf_clearance", "sso_jwt", "__cf_bm"}
    extras = []
    seen = {(item["name"], item["domain"], item["path"]) for item in output}
    for item in list(output):
        name = item["name"]
        if name not in sso_names and not name.startswith("sso"):
            continue
        for domain in (".x.ai", "accounts.x.ai", ".accounts.x.ai", "auth.x.ai", ".auth.x.ai"):
            key = (name, domain, item["path"])
            if key in seen:
                continue
            clone = dict(item)
            clone["domain"] = domain
            extras.append(clone)
            seen.add(key)
    output.extend(extras)
    return output


def inject_cookies(page: Any, cookies: Any, log: Optional[LogFn] = None) -> int:
    logger = log or _noop_log
    items = normalize_cookies(cookies)
    if not items or page is None:
        return 0
    for url in ("https://accounts.x.ai/", "https://auth.x.ai/", "https://grok.com/"):
        try:
            page.get(url)
            _sleep(0.4)
        except Exception:
            continue
    count = 0
    for target_name, target in (("page", page), ("browser", getattr(page, "browser", None))):
        if target is None:
            continue
        try:
            target.set.cookies(items)  # type: ignore[attr-defined]
            count = len(items)
            logger("injected cookies bulk via %s=%s" % (target_name, count))
            break
        except Exception as exc:
            logger("bulk set via %s failed: %s" % (target_name, exc))
    if count == 0:
        for item in items:
            ok = False
            for target in (page, getattr(page, "browser", None)):
                if target is None:
                    continue
                try:
                    target.set.cookies(item)  # type: ignore[attr-defined]
                    ok = True
                    break
                except Exception:
                    continue
            if ok:
                count += 1
        logger("injected cookies one-by-one=%s/%s" % (count, len(items)))
    try:
        javascript_items = [
            cookie
            for cookie in items
            if not bool(cookie.get("httpOnly")) and str(cookie.get("name") or "").startswith("sso")
        ]
        if javascript_items:
            page.run_js(
                """
                const items = arguments[0] || [];
                for (const c of items) {
                  let cookie = `${c.name}=${c.value}; path=${c.path || '/'}; domain=${c.domain || '.x.ai'}`;
                  if (c.secure !== false) cookie += '; Secure';
                  document.cookie = cookie;
                }
                return document.cookie;
                """,
                javascript_items,
            )
            logger("injected non-httpOnly sso cookies via document.cookie")
    except Exception as exc:
        logger("document.cookie injection failed: %s" % exc)
    return count


def acquire_mint_browser(proxy: Optional[str] = None, headless: bool = False, reuse: bool = True, recycle_every: int = 15, log: Optional[LogFn] = None):
    logger = log or _noop_log
    state = _mint_tls_get()
    if not reuse:
        browser, page = create_standalone_page(proxy=proxy, headless=headless, log=logger)
        return browser, page, True
    proxy_key = str(proxy or "")
    if state["browser"] is None:
        browser, page = create_standalone_page(proxy=proxy, headless=headless, log=logger)
        state.update({"browser": browser, "page": page, "served": 0, "proxy": proxy_key, "headless": bool(headless)})
        return browser, page, False
    if state["proxy"] != proxy_key or bool(state["headless"]) != bool(headless):
        try:
            close_standalone(state["browser"])
        except Exception:
            pass
        browser, page = create_standalone_page(proxy=proxy, headless=headless, log=logger)
        state.update({"browser": browser, "page": page, "served": 0, "proxy": proxy_key, "headless": bool(headless)})
        return browser, page, False
    if recycle_every and state["served"] and state["served"] % max(int(recycle_every), 1) == 0:
        try:
            close_standalone(state["browser"])
        except Exception:
            pass
        browser, page = create_standalone_page(proxy=proxy, headless=headless, log=logger)
        state.update({"browser": browser, "page": page, "served": 0, "proxy": proxy_key, "headless": bool(headless)})
        return browser, page, False
    clear_page_session(state["page"], browser=state["browser"], log=logger)
    return state["browser"], state["page"], False


def release_mint_browser(owned: bool, success: bool, log: Optional[LogFn] = None) -> None:
    logger = log or _noop_log
    if owned:
        return
    state = _mint_tls_get()
    if success:
        state["served"] = int(state.get("served", 0) or 0) + 1
        logger("mint browser served=%s" % state["served"])


def shutdown_mint_browsers() -> None:
    state = _mint_tls_get()
    browser = state.get("browser")
    if browser is not None:
        try:
            close_standalone(browser)
        except Exception:
            pass
    state.update({"browser": None, "page": None, "served": 0, "proxy": None, "headless": None})


def _page_url(page: Any) -> str:
    try:
        return str(getattr(page, "url", "") or "")
    except Exception:
        return ""


def _visible_text(page: Any) -> str:
    try:
        return str(page.run_js("return (document.body && (document.body.innerText || document.body.textContent)) || '';"))
    except Exception:
        return ""


def _norm(text: str) -> str:
    return re.sub(r"\s+", " ", str(text or "")).strip()


def _find_button_exact(page: Any, label: str) -> Optional[Any]:
    try:
        return page.ele(
            "xpath://button[normalize-space()='%s'] | //*[@role='button' and normalize-space()='%s'] | //a[normalize-space()='%s'] | //input[@type='submit' and @value='%s']"
            % (label, label, label, label),
            timeout=0.4,
        )
    except Exception:
        return None


def _cookie_banner_visible(text: str) -> bool:
    source = text or ""
    lower = source.lower()
    return any(
        needle in source or needle in lower
        for needle in ("全部允许", "隐私偏好", "cookie", "privacy preference", "allow all")
    )


def _dismiss_cookie_banner(page: Any, log: LogFn) -> bool:
    for label in ("全部允许", "Allow all", "接受", "Accept"):
        if _click_exact(page, [label], log, real=True):
            log("cookie banner dismissed: %s" % label)
            return True
    return False


def _click_exact(page: Any, labels, log: LogFn, real: bool = False) -> Optional[str]:
    for label in labels:
        target = _find_button_exact(page, label)
        if not target:
            continue
        try:
            if real:
                target.click()
            else:
                try:
                    target.click(by_js=True)
                except Exception:
                    target.click()
            log("clicked exact button: %s" % label)
            return label
        except Exception as exc:
            log("button click failed %s: %s" % (label, exc))
    return None


def _wait_turnstile(page: Any, log: LogFn, timeout_sec: float, email: str = "", raise_on_timeout: bool = False) -> bool:
    deadline = time.time() + float(timeout_sec)
    while time.time() < deadline:
        text = _visible_text(page)
        if not _is_turnstile_challenge(text):
            try:
                token_length = page.run_js(
                    """
                    const input = document.querySelector('input[name="cf-turnstile-response"]');
                    return String((input && input.value) || '').trim().length;
                    """
                )
                if int(token_length or 0) >= 80:
                    return True
            except Exception:
                pass
            if not _is_turnstile_challenge(_visible_text(page)):
                return True
        _sleep(1.0)
    if raise_on_timeout:
        shot = _save_debug_shot(page, tag="turnstile-timeout", email=email, log=log)
        message = "turnstile timeout"
        if shot:
            message = "%s shot=%s" % (message, shot)
        raise BrowserConfirmError(message)
    return False


def _fill(page: Any, selector: str, value: str, log: LogFn, label: str = "") -> bool:
    try:
        target = page.ele(selector, timeout=0.8)
    except Exception:
        target = None
    if not target:
        return False
    try:
        target.clear()
    except Exception:
        pass
    try:
        target.input(value)
    except Exception:
        try:
            target.click()
            page.run_js(
                """
                const selector = arguments[0];
                const value = arguments[1];
                const node = document.querySelector(selector);
                if (!node) return false;
                const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
                if (setter) setter.call(node, value);
                else node.value = value;
                node.dispatchEvent(new Event('input', { bubbles: true }));
                node.dispatchEvent(new Event('change', { bubbles: true }));
                return true;
                """,
                selector,
                value,
            )
        except Exception:
            return False
    if label:
        log("filled %s" % label)
    return True


def _fill_input(page: Any, selector: str, value: str, label: str, log: LogFn) -> bool:
    return _fill(page, selector, value, log, label)


def _detect_auth_error(text: str, url: str = "") -> Optional[str]:
    source = text or ""
    lower = source.lower()
    resolved_url = (url or "").lower()
    needles = [
        ("wrong password", "wrong password"),
        ("invalid password", "wrong password"),
        ("incorrect password", "wrong password"),
        ("密码错误", "wrong password"),
        ("wrong email", "wrong email"),
        ("invalid email", "wrong email"),
        ("attention required", "cloudflare challenge/block"),
        ("access denied", "cloudflare blocked / access denied"),
        ("unable to access", "cloudflare blocked / unable to access"),
        ("rate limited", "cloudflare rate limited"),
        ("try again later", "cloudflare rate limited"),
    ]
    for needle, message in needles:
        if needle in lower or needle in source:
            return message
    if "auth.grok.com/set-cookie" in resolved_url and (
        "blocked" in lower or "unable to access" in lower or "cloudflare" in lower
    ):
        return "cloudflare blocked on set-cookie"
    return None


def approve_device_code(
    page: Any,
    verification_uri_complete: str,
    email: str,
    password: str,
    user_code: str = "",
    timeout_sec: float = 240.0,
    stop_event: Optional[threading.Event] = None,
    log: Optional[LogFn] = None,
) -> None:
    logger = log or _noop_log
    if page is None:
        raise BrowserConfirmError("page is None")
    email = (email or "").strip()
    password = password or ""
    if not email or not password:
        raise BrowserConfirmError("email/password required")
    if not user_code and "user_code=" in (verification_uri_complete or ""):
        try:
            user_code = verification_uri_complete.split("user_code=", 1)[1].split("&", 1)[0]
        except Exception:
            user_code = ""
    logger("open device url: %s" % verification_uri_complete)
    try:
        page.get(verification_uri_complete, timeout=60)
    except TypeError:
        page.get(verification_uri_complete)
    _sleep(2.0)

    deadline = time.time() + float(timeout_sec)
    phase = "device"
    login_attempts = 0
    last_url = ""

    while time.time() < deadline:
        if stop_event is not None and stop_event.is_set():
            logger("stop_event set — leave browser loop")
            return
        url = _page_url(page)
        text = _visible_text(page)
        if url != last_url:
            logger("url: %s" % url[:180])
            last_url = url
            snippet = _norm(text)[:160]
            if snippet:
                logger("visible: %s" % snippet)
        auth_error = _detect_auth_error(text, url)
        if auth_error:
            shot = None
            if "block" in auth_error or "cloudflare" in auth_error or "access denied" in auth_error:
                shot = _save_debug_shot(page, tag="cf-block", email=email, log=logger)
            message = auth_error
            if shot:
                message = "%s shot=%s" % (auth_error, shot)
            raise BrowserConfirmError("auth failed: %s" % message)
        if "device/done" in url or "设备已授权" in text or "device authorized" in text.lower():
            logger("device done page — waiting for token poll")
            _sleep(1.5)
            continue
        if "Invalid action" in text:
            page.get(verification_uri_complete)
            _sleep(2.0)
            phase = "device"
            continue
        if _cookie_banner_visible(text):
            if _dismiss_cookie_banner(page, logger):
                _sleep(0.6)
                continue
        if "/consent" in url or "授权 Grok Build" in text or "Authorize Grok Build" in text:
            phase = "consent"
            if _cookie_banner_visible(_visible_text(page)):
                _dismiss_cookie_banner(page, logger)
                _sleep(0.6)
                continue
            if _click_exact(page, ["允许", "Allow", "Authorize", "Approve"], logger, real=True):
                _sleep(2.5)
                continue
            try:
                page.run_js(
                    """
                    const forms = Array.from(document.querySelectorAll('form'));
                    const form = forms.find((x) => {
                      const text = (x.innerText || '');
                      return text.includes('Grok Build') || text.includes('允许') || text.includes('Allow');
                    }) || document.querySelector('form');
                    if (!form) return;
                    const formText = (form.innerText || '');
                    if (formText.includes('隐私偏好') || formText.includes('全部允许') || /cookie/i.test(formText)) return;
                    let actionInput = form.querySelector('input[name=action]');
                    if (!actionInput) {
                      actionInput = document.createElement('input');
                      actionInput.type = 'hidden';
                      actionInput.name = 'action';
                      form.appendChild(actionInput);
                    }
                    actionInput.value = 'allow';
                    const button = [...form.querySelectorAll('button')].find((b) => {
                      const text = (b.innerText || '').trim();
                      return text === '允许' || text === 'Allow' || text === 'Authorize' || text === 'Approve';
                    });
                    if (button) button.click();
                    else form.submit();
                    """
                )
                _sleep(2.5)
            except Exception as exc:
                logger("consent fallback failed: %s" % exc)
            continue
        if page.ele("css:input[name='user_code']", timeout=0.3) and "consent" not in url:
            phase = "device"
            if user_code:
                try:
                    user_code_input = page.ele("css:input[name='user_code']")
                    current = (user_code_input.value or "") if user_code_input else ""
                    if user_code.replace("-", "") not in current.replace("-", ""):
                        user_code_input.clear()
                        user_code_input.input(user_code)
                        logger("filled user_code")
                except Exception:
                    pass
            if _click_exact(page, ["继续", "Continue"], logger, real=False):
                _sleep(2.0)
                continue
            try:
                submit = page.ele("css:button[type='submit']", timeout=0.5)
                if submit:
                    submit.click(by_js=True)
                    _sleep(2.0)
                    continue
            except Exception:
                pass
        if "正在重定向" in text or ("/account" in url and "sign-in" not in url):
            if _click_exact(page, ["继续", "Continue"], logger, real=False):
                _sleep(2.0)
                continue
        if _cookie_banner_visible(text):
            _dismiss_cookie_banner(page, logger)
            _sleep(0.4)
        if "使用邮箱登录" in text or "Continue with email" in text:
            if _click_exact(page, ["使用邮箱登录", "Continue with email", "Sign in with email"], logger, real=False):
                _sleep(1.5)
                phase = "email"
                continue
        if page.ele("css:input[type='email']", timeout=0.3) and not page.ele("css:input[type='password']", timeout=0.2):
            phase = "email"
            _fill(page, "css:input[type='email']", email, logger, "email")
            if _click_exact(page, ["下一步", "Next", "Continue", "继续"], logger, real=False):
                _sleep(1.8)
                continue
        if page.ele("css:input[type='password']", timeout=0.3):
            phase = "password"
            if login_attempts >= 3:
                auth_error = _detect_auth_error(text, url) or "login failed after retries (still on password page)"
                raise BrowserConfirmError("auth failed: %s" % auth_error)
            login_attempts += 1
            _fill(page, "css:input[type='email']", email, logger, "email")
            _wait_turnstile(page, logger, 25, email=email, raise_on_timeout=True)
            _fill(page, "css:input[type='password']", password, logger, "password")
            _wait_turnstile(page, logger, 12, email=email, raise_on_timeout=False)
            if not _click_exact(page, ["登录", "Sign in", "Log in"], logger, real=True):
                try:
                    submit = page.ele("css:button[type='submit']", timeout=0.5) or page.ele("css:button[data-testid='sign-in-submit']", timeout=0.5)
                    if submit:
                        submit.click()
                except Exception as exc:
                    logger("login submit fail: %s" % exc)
            for _ in range(20):
                if stop_event is not None and stop_event.is_set():
                    return
                _sleep(0.5)
                post = _visible_text(page)
                auth_error = _detect_auth_error(post, _page_url(page))
                if auth_error:
                    raise BrowserConfirmError("auth failed: %s" % auth_error)
                if not page.ele("css:input[type='password']", timeout=0.2):
                    break
                if "sign-in" not in _page_url(page):
                    break
            post = _visible_text(page)
            auth_error = _detect_auth_error(post, _page_url(page))
            if auth_error:
                raise BrowserConfirmError("auth failed: %s" % auth_error)
            if page.ele("css:input[type='password']", timeout=0.2) and (_is_turnstile_challenge(post) or login_attempts >= 2):
                shot = _save_debug_shot(page, tag="login-stuck-turnstile", email=email, log=logger)
                message = "turnstile/login stuck after submit"
                if shot:
                    message = "%s shot=%s" % (message, shot)
                raise BrowserConfirmError("auth failed: %s" % message)
            continue
        _sleep(1.0)

    if stop_event is not None and stop_event.is_set():
        return
    shot = _save_debug_shot(page, tag="timeout-phase-%s" % phase, email=email, log=logger)
    message = "browser confirm timeout phase=%s login_attempts=%s" % (phase, login_attempts)
    if shot:
        message = "%s shot=%s" % (message, shot)
    if phase in ("password", "email") or _is_turnstile_challenge(_visible_text(page)):
        raise BrowserConfirmError("auth failed: %s" % message)
    raise BrowserConfirmError(message)


def mint_with_browser(
    email: str,
    password: str,
    page: Optional[Any] = None,
    proxy: Optional[str] = None,
    headless: bool = False,
    browser_timeout_sec: float = 240.0,
    poll_log: Optional[LogFn] = None,
    cancel: Optional[Callable[[], bool]] = None,
    force_standalone: bool = True,
    cookies: Any = None,
    reuse_browser: bool = True,
    recycle_every: int = 15,
):
    from .oauth_device import OAuthDeviceError, poll_device_token, request_device_code
    from .proxyutil import proxy_log_label, resolve_proxy, set_runtime_proxy

    logger = poll_log or _noop_log
    own_browser = None
    owned = False
    work_page = None if force_standalone else page
    resolved = resolve_proxy(proxy)
    set_runtime_proxy(resolved or None)
    success = False
    try:
        last_error = None
        session = None
        for attempt in range(1, 4):
            try:
                session = request_device_code(proxy=resolved or None)
                last_error = None
                break
            except BaseException as exc:
                last_error = exc
                logger("request_device_code attempt %s/3 failed: %s" % (attempt, exc))
                _sleep(1.5 * attempt)
        if session is None:
            raise last_error or RuntimeError("request_device_code failed")
        logger("device user_code=%s expires_in=%s proxy=%s" % (session.user_code, session.expires_in, proxy_log_label(resolved) or "(none)"))
        if work_page is None:
            own_browser, work_page, owned = acquire_mint_browser(
                proxy=resolved or None,
                headless=headless,
                reuse=reuse_browser,
                recycle_every=recycle_every,
                log=logger,
            )
        if cookies:
            injected = inject_cookies(work_page, cookies, log=logger)
            logger("cookie inject count=%s" % injected)
            try:
                work_page.get("https://accounts.x.ai/")
                _sleep(1.0)
                logger("post-inject session url=%s visible=%s" % (_page_url(work_page)[:120], _norm(_visible_text(work_page))[:120]))
            except Exception as exc:
                logger("post-inject check: %s" % exc)
        stop_event = threading.Event()
        token_box = {}
        error_box = {}

        def _poll() -> None:
            try:
                time.sleep(2)
                result = poll_device_token(
                    session.device_code,
                    token_endpoint=session.token_endpoint,
                    interval=max(session.interval, 5),
                    expires_in=min(session.expires_in, int(browser_timeout_sec) + 60),
                    log=logger,
                    cancel=cancel,
                    proxy=resolved or None,
                )
                token_box["token"] = result
                stop_event.set()
                logger("token poll SUCCESS — stop_event set")
            except BaseException as exc:
                error_box["err"] = exc
                stop_event.set()

        thread = threading.Thread(target=_poll, name="oauth-poll", daemon=True)
        thread.start()
        try:
            approve_device_code(
                work_page,
                verification_uri_complete=session.verification_uri_complete,
                email=email,
                password=password,
                user_code=session.user_code,
                timeout_sec=browser_timeout_sec,
                stop_event=stop_event,
                log=logger,
            )
        except BrowserConfirmError as exc:
            lower = str(exc).lower()
            hard = (
                "auth failed" in lower
                or "turnstile" in lower
                or "cloudflare" in lower
                or "blocked" in lower
                or "access denied" in lower
                or "password" in lower
                or "browser confirm timeout" in lower
            )
            if hard:
                stop_event.set()
                raise
        thread.join(timeout=max(browser_timeout_sec, 60) + 30)
        if "token" in token_box:
            token_result = token_box["token"]
            success = True
            return {
                "access_token": token_result.access_token,
                "refresh_token": token_result.refresh_token,
                "id_token": token_result.id_token,
                "token_type": token_result.token_type,
                "expires_in": token_result.expires_in,
                "user_code": session.user_code,
                "token_endpoint": session.token_endpoint,
            }
        if "err" in error_box:
            raise error_box["err"]
        raise OAuthDeviceError("token poll thread ended without result")
    finally:
        if own_browser is not None:
            if owned:
                close_standalone(own_browser)
            else:
                release_mint_browser(owned=False, success=success, log=logger)
