"""Constants for the Gemini web interface.

Endpoints, RPC IDs, model hashes, headers, error codes.
"""

import base64
import hashlib
from enum import IntEnum, StrEnum


class Endpoint(StrEnum):
    """Gemini web interface endpoints."""

    GOOGLE = "https://www.google.com"
    INIT = "https://gemini.google.com/app"
    GENERATE = (
        "https://gemini.google.com/_/BardChatUi/data/"
        "assistant.lamda.BardFrontendService/StreamGenerate"
    )
    BATCH_EXEC = "https://gemini.google.com/_/BardChatUi/data/batchexecute"
    UPLOAD = "https://content-push.googleapis.com/upload"
    ROTATE_COOKIES = "https://accounts.google.com/RotateCookies"
    PROCESS_GEM_FILE = (
        "https://gemini.google.com/_/BardChatUi/data/"
        "assistant.lamda.BardFrontendService/ProcessFile"
    )


class RPC(StrEnum):
    """Google RPC IDs used in the Gemini web interface via batchexecute."""

    # Chat management
    LIST_CHATS = "MaZiqc"
    READ_CHAT = "hNvQHb"
    DELETE_CHAT = "GzXR5e"

    # Gem management
    LIST_GEMS = "CNgdBe"
    CREATE_GEM = "oMH3Zd"
    UPDATE_GEM = "kHv0Vd"
    DELETE_GEM = "UXcSJb"
    LIST_NLM_NOTEBOOKS = "NXpLKc"

    # Activity
    BARD_ACTIVITY = "ESY5D"

    # Status / polling
    STATUS_POLL = "aPya6c"
    ASYNC_POLL = "kwDCne"  # Deep Research progress, video gen completion

    # Post-processing
    POST_PROCESS = "PCck7e"

    # Page init RPCs (discovered from live capture)
    INIT_CONFIG = "otAQ7b"
    INIT_FEATURES = "GPRiHf"
    INIT_SETTINGS = "maGuAc"
    INIT_STATE = "cYRIkd"
    INIT_CAPABILITIES = "ozz5Z"
    SYNC_STATE = "L5adhe"
    USER_PREFS = "K4WWud"
    USAGE_INFO = "qpEbW"
    ACCOUNT_INFO = "o30O0e"
    HISTORY_STATE = "DYBcR"
    NOTIFICATION_STATE = "ku4Jyf"


# Header used to select the model
MODEL_HEADER_KEY = "x-goog-ext-525001261-jspb"

# Additional headers discovered from live capture
TRACKING_HEADER_KEY = "x-goog-ext-73010989-jspb"
SESSION_HEADER_KEY = "x-goog-ext-525005358-jspb"


class Model:
    """Gemini model definitions with hashes for the model selection header."""

    def __init__(self, name: str, hash: str | None = None):
        self.name = name
        self.hash = hash

    @property
    def header(self) -> dict[str, str]:
        """Return the model selection header dict. Empty if no hash (unspecified model)."""
        if not self.hash:
            return {}
        return {
            MODEL_HEADER_KEY: (
                f'[1,null,null,null,"{self.hash}",null,null,0,[4],null,null,2]'
            )
        }

    def __repr__(self) -> str:
        return f"Model(name={self.name!r}, hash={self.hash!r})"

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Model):
            return self.name == other.name
        return NotImplemented

    def __hash__(self) -> int:
        return hash(self.name)


# Known models - hashes captured from live Gemini web UI (Feb 19, 2026)
# The UI shows three modes: Fast, Thinking, Pro
# Pro was upgraded to Gemini 3.1 Pro on Feb 19, 2026 (same hash).
MODELS: dict[str, Model] = {
    "unspecified": Model("unspecified", None),
    # Current models (captured from live UI model picker)
    "fast": Model("fast", "56fdd199312815e2"),
    "thinking": Model("thinking", "e051ce1aa80aa576"),
    "pro": Model("pro", "e6fa609c3fa255c0"),  # Gemini 3.1 Pro (Feb 2026)
}

# Short aliases for convenience (gemcli chat "hi" -m pro)
MODEL_ALIASES: dict[str, str] = {
    "flash": "fast",       # "flash" is a common alias for the fast model
    "preview": "fast",     # backward compat with earlier config
    "default": "fast",
}

DEFAULT_MODEL = MODELS["fast"]

# Models that require BotGuard token (Token Factory) for model switching
TOKEN_FACTORY_MODELS = {"pro", "thinking"}

# Chrome User-Agent for Token Factory requests (macOS Chrome 145)
CHROME_USER_AGENT = (
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
    "AppleWebKit/537.36 (KHTML, like Gecko) "
    "Chrome/145.0.0.0 Safari/537.36"
)

# API key for x-browser-validation computation
XBV_API_KEY = "AIzaSyDr2UxVnv_U85AbhhY8XSHSIavUW0DC-sY"

# Google Drive Picker API key (embedded in Google's public Drive Picker JS library,
# used for clients6.google.com/drive/v2internal calls with SAPISID auth)
DRIVE_PICKER_API_KEY = "AIzaSyAw-cTyp9Xotzvu3vNDWhDU3E9NConkKxQ"

# Chrome-like headers for Token Factory HTTP replay
CHROME_HEADERS: dict[str, str] = {
    "Accept": "*/*",
    "Accept-Language": "en-US,en;q=0.9",
    "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
    "Origin": "https://gemini.google.com",
    "Referer": "https://gemini.google.com/",
    "User-Agent": CHROME_USER_AGENT,
    "X-Same-Domain": "1",
    "x-browser-channel": "stable",
    "x-browser-year": "2026",
    "x-client-data": "CJWJywE=",
    "Sec-Ch-Ua": '"Chromium";v="145", "Google Chrome";v="145", "Not-A.Brand";v="24"',
    "Sec-Ch-Ua-Mobile": "?0",
    "Sec-Ch-Ua-Platform": '"macOS"',
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
}


def compute_xbv(user_agent: str) -> str:
    """Compute x-browser-validation header: BASE64(SHA-1(API_KEY + UA))."""
    return base64.b64encode(
        hashlib.sha1((XBV_API_KEY + user_agent).encode()).digest()
    ).decode()


def get_model(name: str) -> Model:
    """Look up a model by name or alias. Raises ValueError if not found."""
    # Check direct name first
    if name in MODELS:
        return MODELS[name]
    # Check aliases
    if name in MODEL_ALIASES:
        return MODELS[MODEL_ALIASES[name]]
    # List available options
    all_options = sorted(set(list(MODELS.keys()) + list(MODEL_ALIASES.keys())))
    raise ValueError(
        f"Unknown model: {name!r}. Available: {', '.join(all_options)}"
    )


class ErrorCode(IntEnum):
    """Known error codes returned from the Gemini server."""

    TEMPORARY_ERROR = 1013
    USAGE_LIMIT_EXCEEDED = 1037
    MODEL_INCONSISTENT = 1050
    MODEL_HEADER_INVALID = 1052
    IP_TEMPORARILY_BLOCKED = 1060


# Standard headers for Gemini requests
GEMINI_HEADERS: dict[str, str] = {
    "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
    "Host": "gemini.google.com",
    "Origin": "https://gemini.google.com",
    "Referer": "https://gemini.google.com/",
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/145.0.0.0 Safari/537.36"
    ),
    "X-Same-Domain": "1",
}

ROTATE_COOKIES_HEADERS: dict[str, str] = {
    "Content-Type": "application/json",
}

UPLOAD_HEADERS: dict[str, str] = {
    "Push-ID": "feeds/mcudyrk2a4khkz",
}

# Regex patterns for extracting tokens from the Gemini init page HTML
TOKEN_PATTERNS: dict[str, str] = {
    "snlm0e": r'"SNlM0e":\s*"(.*?)"',
    "cfb2h": r'"cfb2h":\s*"(.*?)"',
    "fdrfje": r'"FdrFJe":\s*"(.*?)"',
}

# Regex pattern for extracting user email from WIZ_global_data.oPEP7c
EMAIL_PATTERN = r'"oPEP7c":\s*"([^"]+@[^"]+)"'

# Cookie names
COOKIE_1PSID = "__Secure-1PSID"
COOKIE_1PSIDTS = "__Secure-1PSIDTS"
COOKIE_DOMAIN = ".google.com"

# Auto-refresh interval for cookies (seconds)
COOKIE_REFRESH_INTERVAL = 540  # 9 minutes
COOKIE_REFRESH_COOLDOWN = 60  # Minimum seconds between refresh attempts

# Rotate cookies request body
ROTATE_COOKIES_BODY = '[000,"-0000000000000000000"]'

# Config paths
CONFIG_DIR_NAME = ".gemini-web-mcp-cli"
NLM_CONFIG_DIR_NAME = ".notebooklm-mcp-cli"
PROFILES_DIR = "profiles"
CONFIG_FILE = "config.json"
AUTH_FILE = "auth.json"

# Environment variables
ENV_PROFILE = "GEMCLI_PROFILE"
DEFAULT_PROFILE_NAME = "default"

# Request ID management
REQID_INCREMENT = 100000

# StreamGenerate inner_req_list size
INNER_REQ_LIST_SIZE = 70

# Tool activation IDs for inner_req_list[49]
VIDEO_TOOL_ID = 11  # Activates Veo 3.1 video generation
IMAGE_TOOL_ID = 14  # Activates image generation — Nano Banana 2 (Flash) and Nano Banana Pro
MUSIC_TOOL_ID = 21  # Activates Lyria 3 music generation

# Response parsing paths
RESPONSE_PATHS = {
    "inner_json": [2],
    "metadata": [1],
    "candidates": [4],
    "completion": [25],
    "error_code": [5, 2, 0, 1, 0],
    # Per candidate
    "candidate_rcid": [0],
    "candidate_text": [1, 0],
    "candidate_thoughts": [37, 0, 0],
    "candidate_web_images": [12, 1],
    "candidate_generated_images": [12, 7, 0],
    "candidate_generated_music": [12, 86],
    "candidate_generated_video": [12, 59],
}
