"""Optional post-registration CPA/OIDC export hook."""
from dataclasses import dataclass
import importlib.util
import os
import shutil
import sys
import time
from pathlib import Path
from typing import Optional

_ROOT = Path(__file__).resolve().parent
_DEFAULT_AUTH_DIR = _ROOT / "cpa_auths"


@dataclass(frozen=True)
class CpaExportSettings:
    enabled: bool
    auth_dir: Path
    hotload_dir: Optional[Path]
    copy_to_hotload: bool
    proxy: str
    headless: bool
    mint_timeout: float
    request_timeout: float
    poll_timeout: float
    base_url: str
    force_standalone: bool
    cookie_inject: bool
    tools_dir: str

    @classmethod
    def from_config(cls, config):
        cfg = dict(config or {})
        auth_dir = Path(cfg.get("cpa_auth_dir") or _DEFAULT_AUTH_DIR).expanduser()
        if not auth_dir.is_absolute():
            auth_dir = (_ROOT / auth_dir).resolve()
        hotload_value = str(cfg.get("cpa_hotload_dir") or "").strip()
        hotload_dir = Path(hotload_value).expanduser() if hotload_value else None
        if hotload_dir is not None and not hotload_dir.is_absolute():
            hotload_dir = (_ROOT / hotload_dir).resolve()
        return cls(
            enabled=bool(cfg.get("cpa_export_enabled", True)),
            auth_dir=auth_dir,
            hotload_dir=hotload_dir,
            copy_to_hotload=bool(cfg.get("cpa_copy_to_hotload", False)),
            proxy=str(cfg.get("cpa_proxy") or cfg.get("proxy") or "").strip(),
            headless=bool(cfg.get("cpa_headless", False)),
            mint_timeout=float(cfg.get("cpa_mint_timeout_sec") or 300),
            request_timeout=float(cfg.get("cpa_oidc_request_timeout_sec") or 15),
            poll_timeout=float(cfg.get("cpa_oidc_poll_timeout_sec") or 15),
            base_url=str(cfg.get("cpa_base_url") or "https://cli-chat-proxy.grok.com/v1").strip(),
            force_standalone=bool(cfg.get("cpa_force_standalone", True)),
            cookie_inject=bool(cfg.get("cpa_mint_cookie_inject", True)),
            tools_dir=str(cfg.get("api_reverse_tools") or "").strip(),
        )


def _load_mint_and_export(tools_dir=""):
    tools_value = str(tools_dir or "").strip()
    if not tools_value:
        from cpa_xai import mint_and_export
        return mint_and_export
    tools = Path(tools_value).expanduser().resolve()
    package = tools if tools.name == "cpa_xai" else tools / "cpa_xai"
    init_path = package / "__init__.py"
    if package.resolve() == (_ROOT / "cpa_xai").resolve():
        from cpa_xai import mint_and_export
        return mint_and_export
    if not init_path.is_file():
        raise ImportError("cpa_xai package not found under %s" % tools)
    module_name = "_external_cpa_xai_%s" % abs(hash(str(package)))
    spec = importlib.util.spec_from_file_location(
        module_name,
        str(init_path),
        submodule_search_locations=[str(package)],
    )
    if spec is None or spec.loader is None:
        raise ImportError("unable to load cpa_xai from %s" % package)
    module = importlib.util.module_from_spec(spec)
    sys.modules[module_name] = module
    try:
        spec.loader.exec_module(module)
    except Exception:
        sys.modules.pop(module_name, None)
        raise
    return module.mint_and_export


def export_cookies_from_page(page):
    if page is None:
        return []
    cookies = None
    for getter in (
        lambda: page.cookies(all_domains=True, all_info=True),
        lambda: page.cookies(all_domains=True),
        lambda: page.cookies(),
    ):
        try:
            cookies = getter()
            if cookies:
                break
        except TypeError:
            continue
        except Exception:
            continue
    if not cookies:
        try:
            browser = getattr(page, "browser", None)
            if browser is not None:
                cookies = browser.cookies()
        except Exception:
            cookies = None
    return [item for item in cookies if isinstance(item, dict)] if isinstance(cookies, list) else []


def _normalize_result(result, email=""):
    value = dict(result or {})
    value.setdefault("ok", False)
    value.setdefault("skipped", False)
    value.setdefault("email", str(email or ""))
    value.setdefault("error", None)
    return value


def export_cpa_xai_for_account(email, password, page=None, cookies=None, sso=None,
                               config=None, log_callback=None, cancel_callback=None):
    settings = CpaExportSettings.from_config(config)
    log = log_callback or (lambda message: None)
    if not settings.enabled:
        return _normalize_result({"ok": False, "skipped": True, "reason": "disabled"}, email)
    try:
        mint_and_export = _load_mint_and_export(settings.tools_dir)
    except Exception as exc:
        log("[cpa] import cpa_xai failed: %s" % exc)
        return _normalize_result({"ok": False, "error": "import: %s" % exc}, email)

    use_cookies = cookies
    if use_cookies is None and settings.cookie_inject and page is not None:
        use_cookies = export_cookies_from_page(page)
    if not settings.cookie_inject:
        use_cookies = None
    elif sso:
        base = list(use_cookies) if isinstance(use_cookies, list) else []
        sso_value = str(sso).strip()
        for cookie_name in ("sso", "sso-rw"):
            for domain in (".x.ai", "accounts.x.ai", ".accounts.x.ai", "auth.x.ai", ".auth.x.ai", "grok.com", ".grok.com"):
                base.append({"name": cookie_name, "value": sso_value, "domain": domain,
                             "path": "/", "secure": True, "httpOnly": True})
        use_cookies = base

    settings.auth_dir.mkdir(parents=True, exist_ok=True)
    log("[cpa] mint OIDC for %s -> %s" % (email, settings.auth_dir))
    result = mint_and_export(
        email=email, password=password, auth_dir=settings.auth_dir,
        page=None if settings.force_standalone else page,
        proxy=settings.proxy or None, headless=settings.headless,
        base_url=settings.base_url, browser_timeout_sec=settings.mint_timeout,
        force_standalone=settings.force_standalone, cookies=use_cookies,
        reuse_browser=True, recycle_every=15,
        log=lambda message: log("[cpa] %s" % message), cancel=cancel_callback,
        request_timeout_sec=settings.request_timeout,
        poll_timeout_sec=settings.poll_timeout,
    )
    result = _normalize_result(result, email)
    if result.get("ok") and result.get("path") and settings.copy_to_hotload and settings.hotload_dir:
        try:
            settings.hotload_dir.mkdir(parents=True, exist_ok=True)
            source = Path(result["path"])
            target = settings.hotload_dir / source.name
            shutil.copy2(str(source), str(target))
            try:
                os.chmod(str(target), 0o600)
            except Exception:
                pass
            result["hotload_path"] = str(target)
            log("[cpa] hotload copy -> %s" % target)
        except Exception as exc:
            result["cpa_copy_error"] = str(exc)
            result["warning"] = True
            result["partial"] = True
            log("[cpa] hotload copy failed: %s" % exc)
    if not result.get("ok"):
        fail_path = settings.auth_dir / "cpa_auth_failed.txt"
        try:
            with open(str(fail_path), "a", encoding="utf-8") as handle:
                handle.write("%s----%s----%s\n" % (email, result.get("error") or "unknown", int(time.time())))
        except Exception as exc:
            log("[cpa] failed to persist failure record: %s" % exc)
    return result
