"""Browser session health checker for persistent authenticated sessions.

Validates whether browser auth sessions are still active by navigating to
target domains and checking for known authenticated-state indicators vs
login wall patterns. Reuses patterns from intervention_detector.py.
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Awaitable


class SessionStatus(str, Enum):
    AUTHENTICATED = "authenticated"
    EXPIRED = "expired"
    UNKNOWN = "unknown"
    ERROR = "error"


@dataclass(frozen=True)
class SessionCheckResult:
    """Result of checking authentication status for a single domain."""

    domain: str
    status: SessionStatus
    indicator: str  # What text/element determined the status
    detail: str = ""


# ---------------------------------------------------------------------------
# Login-wall patterns (aligned with intervention_detector.py LOGIN_WALL_PATTERNS)
# ---------------------------------------------------------------------------
_LOGIN_WALL_RAW = [
    r"sign\s*in\s*to\s*continue",
    r"log\s*in\s*to\s*continue",
    r"login\s*required",
    r"session\s*(has\s*)?expired",
    r"session\s*timed\s*out",
    r"please\s*log\s*in",
    r"please\s*sign\s*in",
    r"you\s*need\s*to\s*sign\s*in",
    r"you\s*must\s*be\s*logged\s*in",
    r"create\s*an?\s*account",
    r"log\s*in\s*to\s*your\s*account",
    r"your\s*session\s*expired",
]

LOGIN_WALL_PATTERNS: list[re.Pattern[str]] = [
    re.compile(p, re.IGNORECASE) for p in _LOGIN_WALL_RAW
]

# ---------------------------------------------------------------------------
# Per-domain indicators that the user is currently authenticated.
# At least one indicator must appear in the visible page text.
# ---------------------------------------------------------------------------
AUTHENTICATED_INDICATORS: dict[str, list[str]] = {
    "linkedin.com": ["Start a post", "My Network", "Messaging"],
    "gmail.com": ["Compose", "Inbox", "Primary"],
    "mail.google.com": ["Compose", "Inbox", "Primary"],
    "twitter.com": ["Post", "What is happening", "Home"],
    "x.com": ["Post", "What is happening", "Home"],
    "facebook.com": ["Create post", "What's on your mind", "Marketplace"],
    "instagram.com": ["Create", "Explore", "Messages"],
    "alibaba.com": ["My Alibaba", "Message Center", "RFQ"],
    "github.com": ["Pull requests", "Issues", "Explore"],
    "reddit.com": ["Create Post", "Get Coins", "Popular"],
}

# Maximum bytes of page text we inspect (keeps memory bounded).
_MAX_TEXT_SCAN = 5000


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

async def check_session_health(
    page_getter: Callable[[], Awaitable[Any]],
    domains: list[str],
    timeout_ms: int = 10_000,
) -> list[SessionCheckResult]:
    """Check if browser sessions are still authenticated for given domains.

    Args:
        page_getter: Async callable returning a Playwright-compatible page.
        domains: List of domains to check.
        timeout_ms: Timeout per domain check in milliseconds.

    Returns:
        List of ``SessionCheckResult``, one per domain.
    """
    if not domains:
        return []

    page = await page_getter()
    results: list[SessionCheckResult] = []
    for domain in domains:
        result = await _check_single_domain(page, domain, timeout_ms)
        results = [*results, result]
    return results


async def _check_single_domain(
    page: Any,
    domain: str,
    timeout_ms: int,
) -> SessionCheckResult:
    """Check authentication status for a single domain."""
    clean_domain = _normalize_domain(domain)
    url = f"https://www.{clean_domain}"

    try:
        await page.goto(url, wait_until="domcontentloaded", timeout=timeout_ms)
        # Best-effort wait for JS-rendered content.
        try:
            await page.wait_for_load_state(
                "networkidle", timeout=min(timeout_ms, 5000)
            )
        except Exception:
            pass

        page_text: str = await page.evaluate("() => document.body?.innerText || ''")
        page_url: str = page.url
        text_window = page_text[:_MAX_TEXT_SCAN]

        # 1. Check for login wall first (takes priority).
        for pattern in LOGIN_WALL_PATTERNS:
            if pattern.search(text_window):
                return SessionCheckResult(
                    domain=clean_domain,
                    status=SessionStatus.EXPIRED,
                    indicator=pattern.pattern,
                    detail=f"Login wall detected at {page_url}",
                )

        # 2. Check for known authenticated indicators.
        indicators = AUTHENTICATED_INDICATORS.get(clean_domain, [])
        for indicator in indicators:
            if indicator.lower() in text_window.lower():
                return SessionCheckResult(
                    domain=clean_domain,
                    status=SessionStatus.AUTHENTICATED,
                    indicator=indicator,
                    detail=f"Found '{indicator}' at {page_url}",
                )

        # 3. Indicators defined but none matched -> likely expired.
        if indicators:
            return SessionCheckResult(
                domain=clean_domain,
                status=SessionStatus.EXPIRED,
                indicator="none_matched",
                detail=f"No authenticated indicators found at {page_url}",
            )

        # 4. Unknown domain — no indicators configured.
        return SessionCheckResult(
            domain=clean_domain,
            status=SessionStatus.UNKNOWN,
            indicator="no_indicators",
            detail=f"No indicators configured for {clean_domain}",
        )

    except Exception as exc:
        return SessionCheckResult(
            domain=clean_domain,
            status=SessionStatus.ERROR,
            indicator="exception",
            detail=str(exc)[:200],
        )


# ---------------------------------------------------------------------------
# Utilities
# ---------------------------------------------------------------------------

def _normalize_domain(raw: str) -> str:
    """Lowercase, strip whitespace, and remove ``www.`` prefix."""
    return raw.lower().strip().removeprefix("www.").removeprefix("https://").removeprefix("http://").split("/")[0]


_URL_RE = re.compile(
    r"https?://(?:www\.)?([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", re.IGNORECASE
)
_BARE_DOMAIN_RE = re.compile(
    r"\b([a-zA-Z0-9-]+\.(?:com|org|net|io|co|ai|dev))\b", re.IGNORECASE
)


def extract_domains_from_task(task_description: str) -> list[str]:
    """Extract likely target domains from a task description.

    Scans for explicit URLs and bare domain-like tokens.
    Returns a sorted, deduplicated list of lowercase domains.
    """
    if not task_description:
        return []

    domains: set[str] = set()
    for match in _URL_RE.finditer(task_description):
        domains.add(match.group(1).lower())
    for match in _BARE_DOMAIN_RE.finditer(task_description):
        domains.add(match.group(1).lower())

    return sorted(domains)
