"""Session health checks — cookie/localStorage inspection for authentication status.

Replaces text-based heuristics with direct cookie and localStorage inspection.
Used by the orchestrator to decide whether re-auth is needed before running tasks.
"""

from __future__ import annotations

import time
from dataclasses import dataclass, field
from typing import Any

from loguru import logger

# Known session cookie name patterns per domain
_DOMAIN_SESSION_COOKIES: dict[str, list[str]] = {
    "linkedin.com": ["li_at", "JSESSIONID", "li_mc"],
    "gmail.com": ["SSID", "SID", "HSID", "APISID"],
    "x.com": ["auth_token", "ct0"],
    "twitter.com": ["auth_token", "ct0"],
    "instagram.com": ["sessionid", "csrftoken", "ds_user_id"],
    "facebook.com": ["c_user", "xs", "datr"],
    "alibaba.com": ["ali_apache_track", "cna", "ali_bav"],
}

# Generic session cookie name substrings
_GENERIC_SESSION_NAMES = frozenset([
    "session", "sid", "ssid", "token", "auth", "jwt",
    "li_at", "jsessionid", "csrf", "xsrf",
])


@dataclass(frozen=True)
class SessionHealthResult:
    """Result of a session health check."""
    domain: str
    is_authenticated: bool
    login_wall_detected: bool = False
    session_cookies_found: int = 0
    auth_tokens_found: int = 0
    expired_cookies: list[str] = field(default_factory=list)
    error: str | None = None
    checked_at: float = field(default_factory=time.time)


def _is_session_cookie(cookie: dict, domain: str | None = None) -> bool:
    """Heuristic: is this a session cookie?"""
    name_lower = cookie.get("name", "").lower()

    # Check domain-specific cookies first
    if domain:
        domain_stripped = domain.lstrip("www.").lower()
        for known_domain, known_cookies in _DOMAIN_SESSION_COOKIES.items():
            if known_domain in domain_stripped:
                if any(c.lower() == name_lower for c in known_cookies):
                    return True

    # Generic check
    return any(n in name_lower for n in _GENERIC_SESSION_NAMES)


def _is_cookie_expired(cookie: dict) -> bool:
    """Check if a cookie has expired."""
    expires = cookie.get("expires", -1)
    if expires <= 0:
        # Session cookie (no expiry) or unset — not expired
        return False
    return expires < time.time()


async def check_session_cookies(page: Any, domain: str) -> SessionHealthResult:
    """Check if session cookies exist and are not expired.

    Args:
        page: Playwright page object
        domain: Domain to check, e.g. "linkedin.com"

    Returns:
        SessionHealthResult with cookie inspection details
    """
    try:
        cookies = await page.context.cookies(f"https://{domain}")
        session_cookies = [c for c in cookies if _is_session_cookie(c, domain)]
        expired = [c["name"] for c in session_cookies if _is_cookie_expired(c)]
        active_session_cookies = len(session_cookies) - len(expired)

        # Also check localStorage for auth tokens
        token_result = await check_local_storage(page, domain)
        auth_token_count = sum(1 for v in token_result.values() if v == "present")

        is_authenticated = active_session_cookies > 0 or auth_token_count > 0

        return SessionHealthResult(
            domain=domain,
            is_authenticated=is_authenticated,
            session_cookies_found=active_session_cookies,
            auth_tokens_found=auth_token_count,
            expired_cookies=expired,
        )
    except Exception as exc:
        logger.warning("Session cookie check failed for {}: {}", domain, exc)
        return SessionHealthResult(
            domain=domain,
            is_authenticated=False,
            error=str(exc),
        )


async def check_local_storage(page: Any, domain: str) -> dict[str, str]:
    """Check localStorage for auth tokens.

    Returns a dict of key -> "present"|"empty" for auth-related keys.
    """
    try:
        tokens: dict[str, str] = await page.evaluate("""() => {
            const patterns = ['token', 'access_token', 'auth_token', 'session', 'jwt', 'refresh_token', 'id_token'];
            const result = {};
            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                const lower = key.toLowerCase();
                for (const pattern of patterns) {
                    if (lower.includes(pattern)) {
                        result[key] = localStorage.getItem(key) ? 'present' : 'empty';
                        break;
                    }
                }
            }
            return result;
        }""")
        return tokens
    except Exception as exc:
        logger.warning("localStorage check failed for {}: {}", domain, exc)
        return {}


async def check_login_wall(page: Any) -> bool:
    """Check if the current page shows a login wall.

    Uses common indicators: login forms, redirect to auth pages, etc.
    """
    try:
        url = page.url.lower()
        login_url_patterns = [
            "/login", "/signin", "/sign-in", "/auth",
            "/sso", "/oauth", "accounts.google.com",
            "login.microsoftonline.com",
        ]
        if any(p in url for p in login_url_patterns):
            return True

        # Check for login form elements
        has_login_form: bool = await page.evaluate("""() => {
            const forms = document.querySelectorAll('form');
            for (const form of forms) {
                const action = (form.action || '').toLowerCase();
                const inputs = form.querySelectorAll('input[type="password"], input[name*="password"]');
                if (inputs.length > 0 || action.includes('login') || action.includes('signin')) {
                    return true;
                }
            }
            // Check for common login button text
            const buttons = document.querySelectorAll('button, a, input[type="submit"]');
            for (const btn of buttons) {
                const text = (btn.textContent || btn.value || '').toLowerCase().trim();
                if (text === 'sign in' || text === 'log in' || text === 'login') {
                    return true;
                }
            }
            return false;
        }""")
        return has_login_form
    except Exception as exc:
        logger.warning("Login wall check failed: {}", exc)
        return False


async def check_domain_session(page: Any, domain: str) -> SessionHealthResult:
    """Full session health check: cookies + localStorage + login wall detection.

    This is the primary entry point for agent session checks.
    """
    cookie_result = await check_session_cookies(page, domain)

    if cookie_result.error:
        return cookie_result

    login_wall = await check_login_wall(page)

    # If we have cookies but hit a login wall, session may be expired
    is_authenticated = cookie_result.is_authenticated and not login_wall

    return SessionHealthResult(
        domain=domain,
        is_authenticated=is_authenticated,
        login_wall_detected=login_wall,
        session_cookies_found=cookie_result.session_cookies_found,
        auth_tokens_found=cookie_result.auth_tokens_found,
        expired_cookies=cookie_result.expired_cookies,
    )
