"""
NotebookLM auth: Playwright storage → cookies → httpx fetch for SNlM0e / FdrFJe.

Patterns for cookies and HTML token extraction align with the approach used in
`notebooklm-py` (teng-lin/notebooklm-py); adapted for Folio paths and sync httpx.
"""

from __future__ import annotations

import json
import logging
import os
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import httpx

from folio_notion.integrations.notebooklm.paths import get_storage_path
from folio_notion.integrations.notebooklm.regional_google import GOOGLE_REGIONAL_CCTLDS
from folio_notion.integrations.notebooklm.url_utils import (
    contains_google_auth_redirect,
    is_google_auth_redirect,
)

logger = logging.getLogger(__name__)

MINIMUM_REQUIRED_COOKIES = frozenset({"SID"})
ALLOWED_COOKIE_DOMAINS = frozenset(
    {
        ".google.com",
        "notebooklm.google.com",
        ".googleusercontent.com",
    }
)


def _is_google_domain(domain: str) -> bool:
    if domain == ".google.com":
        return True
    if domain.startswith(".google."):
        suffix = domain[8:]
        return suffix in GOOGLE_REGIONAL_CCTLDS
    return False


def _is_allowed_auth_domain(domain: str) -> bool:
    return domain in ALLOWED_COOKIE_DOMAINS or _is_google_domain(domain)


def extract_cookies_from_storage(storage_state: dict[str, Any]) -> dict[str, str]:
    cookies: dict[str, str] = {}
    cookie_domains: dict[str, str] = {}
    for cookie in storage_state.get("cookies", []):
        domain = cookie.get("domain", "")
        name = cookie.get("name")
        if not _is_allowed_auth_domain(domain) or not name:
            continue
        is_base_domain = domain == ".google.com"
        if name not in cookies or is_base_domain:
            if name in cookies and is_base_domain:
                logger.debug(
                    "Cookie %s: prefer .google.com over %s",
                    name,
                    cookie_domains.get(name),
                )
            cookies[name] = cookie.get("value", "")
            cookie_domains[name] = domain
        else:
            logger.debug(
                "Cookie %s: skip duplicate from %s (kept %s)",
                name,
                domain,
                cookie_domains.get(name),
            )

    missing = MINIMUM_REQUIRED_COOKIES - set(cookies.keys())
    if missing:
        all_domains = {c.get("domain", "") for c in storage_state.get("cookies", [])}
        google_domains = sorted(d for d in all_domains if "google" in d.lower())
        parts = [f"Missing required cookies: {missing}"]
        if google_domains:
            parts.append(f"Google domains in storage: {google_domains}")
        parts.append("Run `fn connect notebooklm` to sign in again.")
        raise ValueError("\n".join(parts))
    return cookies


def _load_storage_state(path: Path | None) -> dict[str, Any]:
    if path:
        if not path.exists():
            raise FileNotFoundError(
                f"Storage file not found: {path}\nRun `fn connect notebooklm` first."
            )
        return json.loads(path.read_text(encoding="utf-8"))

    if "NOTEBOOKLM_AUTH_JSON" in os.environ:
        raw = os.environ["NOTEBOOKLM_AUTH_JSON"].strip()
        if not raw:
            raise ValueError("NOTEBOOKLM_AUTH_JSON is set but empty.")
        data = json.loads(raw)
        if not isinstance(data, dict) or "cookies" not in data:
            raise ValueError("NOTEBOOKLM_AUTH_JSON must be Playwright storage JSON with 'cookies'.")
        return data

    sp = get_storage_path()
    if not sp.exists():
        raise FileNotFoundError(
            f"Storage file not found: {sp}\nRun `fn connect notebooklm` first."
        )
    return json.loads(sp.read_text(encoding="utf-8"))


def load_auth_from_storage(path: Path | None = None) -> dict[str, str]:
    storage = _load_storage_state(path)
    return extract_cookies_from_storage(storage)


def extract_csrf_from_html(html: str, final_url: str = "") -> str:
    match = re.search(r'"SNlM0e"\s*:\s*"([^"]+)"', html)
    if not match:
        if is_google_auth_redirect(final_url) or contains_google_auth_redirect(html):
            raise ValueError(
                "Session expired or not signed in. Run `fn connect notebooklm` again."
            )
        raise ValueError(
            f"CSRF token (SNlM0e) not found. Final URL: {final_url or '(unknown)'}"
        )
    return match.group(1)


def extract_session_id_from_html(html: str, final_url: str = "") -> str:
    match = re.search(r'"FdrFJe"\s*:\s*"([^"]+)"', html)
    if not match:
        if is_google_auth_redirect(final_url) or contains_google_auth_redirect(html):
            raise ValueError(
                "Session expired or not signed in. Run `fn connect notebooklm` again."
            )
        raise ValueError(
            f"Session id (FdrFJe) not found. Final URL: {final_url or '(unknown)'}"
        )
    return match.group(1)


def fetch_tokens_sync(cookies: dict[str, str]) -> tuple[str, str]:
    """GET notebooklm.google.com and parse WIZ_global_data tokens."""
    cookie_header = "; ".join(f"{k}={v}" for k, v in cookies.items())
    with httpx.Client(follow_redirects=True, timeout=30.0) as client:
        response = client.get(
            "https://notebooklm.google.com/",
            headers={"Cookie": cookie_header},
        )
        response.raise_for_status()
        final_url = str(response.url)
    if is_google_auth_redirect(final_url):
        raise ValueError(
            "Redirected to Google sign-in. Run `fn connect notebooklm` again."
        )
    html = response.text
    csrf = extract_csrf_from_html(html, final_url)
    session_id = extract_session_id_from_html(html, final_url)
    return csrf, session_id


@dataclass
class AuthTokens:
    """Cookies + NotebookLM RPC tokens (browser handoff → API-ready)."""

    cookies: dict[str, str]
    csrf_token: str
    session_id: str

    @classmethod
    def from_storage(cls, path: Path | None = None) -> AuthTokens:
        cookies = load_auth_from_storage(path)
        csrf, sid = fetch_tokens_sync(cookies)
        return cls(cookies=cookies, csrf_token=csrf, session_id=sid)
