#!/usr/bin/env python3
"""
Madpod agent hook: intervention-detector
Detects CAPTCHA, 2FA prompts, login walls, and other situations requiring human intervention.
Sends intervention alerts via the Madpod WebSocket channel and pauses the agent loop.

Usage: set as madpod-agent hook for PostToolUse events on browser/navigation tools.
"""

import json
import logging
import os
import re
import sys
from datetime import datetime, timezone

logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger("intervention-detector")

AGENT_ID = os.environ.get("MADPOD_AGENT_ID", "unknown")

# Domains commonly requiring authentication — configurable via env var
_DEFAULT_AUTH_DOMAINS = "linkedin.com,twitter.com,x.com,facebook.com,instagram.com"
AUTH_DOMAINS = frozenset(
    d.strip().lower()
    for d in os.environ.get("MADPOD_AUTH_DOMAINS", _DEFAULT_AUTH_DOMAINS).split(",")
    if d.strip()
)

# Patterns that indicate a CAPTCHA
CAPTCHA_PATTERNS = [
    r"captcha",
    r"are you a robot",
    r"verify you are human",
    r"cloudflare.*challenge",
    r"recaptcha",
    r"hcaptcha",
    r"turnstile",
    r"please complete the security check",
    r"security verification",
    r"i'm not a robot",
]

# Patterns that indicate a 2FA / OTP prompt
TWO_FA_PATTERNS = [
    r"two.?factor",
    r"two.?step",
    r"verification code",
    r"authenticator app",
    r"enter the code",
    r"we sent a code to",
    r"check your (email|phone|sms)",
    r"one.?time password",
    r"\botp\b",
    r"security code",
]

# Patterns that indicate a login wall
LOGIN_WALL_PATTERNS = [
    r"sign in to continue",
    r"log in to continue",
    r"please sign in",
    r"please log in",
    r"login required",
    r"you must be logged in",
    r"session (has |)(expired|timed out)",
    r"your session expired",
]

# Phrases that often sound like "verification" work but do not imply the user
# must intervene in the browser. These should remain ordinary work feedback.
NON_BLOCKING_VERIFICATION_PATTERNS = [
    r"contact list needs (to be )?verified",
    r"verify (the )?(contacts?|contact list|results?|list|data|leads?)",
    r"(contacts?|results?|list|data|leads?) (still )?(need|needs) (review|verification|validation)",
    r"(review|validate|verify) the (contacts?|results?|list|data|leads?)",
]

COMPILED_CAPTCHA = [re.compile(p, re.IGNORECASE) for p in CAPTCHA_PATTERNS]
COMPILED_TWO_FA = [re.compile(p, re.IGNORECASE) for p in TWO_FA_PATTERNS]
COMPILED_LOGIN = [re.compile(p, re.IGNORECASE) for p in LOGIN_WALL_PATTERNS]
COMPILED_NON_BLOCKING_VERIFICATION = [re.compile(p, re.IGNORECASE) for p in NON_BLOCKING_VERIFICATION_PATTERNS]


def _detect_intervention_type(text: str) -> str | None:
    """Return the type of intervention detected, or None."""
    for pattern in COMPILED_CAPTCHA:
        if pattern.search(text):
            return "captcha"
    for pattern in COMPILED_TWO_FA:
        if pattern.search(text):
            return "two_fa"
    for pattern in COMPILED_LOGIN:
        if pattern.search(text):
            return "login_wall"
    if any(pattern.search(text) for pattern in COMPILED_NON_BLOCKING_VERIFICATION):
        return None
    return None


def _send_intervention_alert(intervention_type: str, details: str) -> None:
    """Send an intervention alert via the Madpod channel socket."""
    event = {
        "type": "intervention",
        "agent_id": AGENT_ID,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "requires_human": True,
        "intervention_type": intervention_type,
        "content": details,
    }

    channel_socket = os.environ.get("MADPOD_CHANNEL_SOCKET", f"/tmp/madpod-{AGENT_ID}.sock")
    try:
        import socket as sock_module
        with sock_module.socket(sock_module.AF_UNIX, sock_module.SOCK_STREAM) as s:
            s.settimeout(2.0)
            s.connect(channel_socket)
            s.sendall(json.dumps(event).encode() + b"\n")
        logger.info(f"Sent intervention alert: {intervention_type}")
    except Exception as e:
        logger.debug(f"Failed to send intervention alert: {e}")

    # Also write to log
    try:
        from pathlib import Path
        log_dir = Path(f"/mnt/agent/{AGENT_ID}/logs")
        log_dir.mkdir(parents=True, exist_ok=True)
        with open(log_dir / "interventions.jsonl", "a") as f:
            f.write(json.dumps(event) + "\n")
    except Exception:
        pass


def process_hook_event(raw_input: str) -> None:
    """Check tool output for intervention signals."""
    try:
        data = json.loads(raw_input)
    except json.JSONDecodeError:
        print(json.dumps({"continue": True}))
        return

    hook_type = data.get("hook_event_name", "")
    tool_name = data.get("tool_name", "")

    # Only check browser-related tools
    browser_tools = {
        "browser_navigate", "browser_click", "browser_type",
        "navigate", "click", "type", "screenshot", "web_search",
        "computer_use", "bash",
    }
    if tool_name.lower() not in browser_tools and "browser" not in tool_name.lower():
        print(json.dumps({"continue": True}))
        return

    # PreToolUse: warn if navigating to a known auth-required domain
    if hook_type == "PreToolUse" and tool_name.lower() in ("browser_navigate", "navigate"):
        tool_input = data.get("tool_input", {})
        url = ""
        if isinstance(tool_input, dict):
            url = str(tool_input.get("url") or tool_input.get("uri") or "")
        elif isinstance(tool_input, str):
            url = tool_input
        domain = _extract_domain(url)
        if domain and any(domain == auth_d or domain.endswith("." + auth_d) for auth_d in AUTH_DOMAINS):
            _send_auth_domain_warning(domain, url)
        print(json.dumps({"continue": True}))
        return

    # Check tool response text for intervention signals (PostToolUse)
    tool_response = data.get("tool_response", {})
    response_text = ""

    if isinstance(tool_response, str):
        response_text = tool_response
    elif isinstance(tool_response, dict):
        response_text = json.dumps(tool_response)

    if not response_text:
        print(json.dumps({"continue": True}))
        return

    intervention_type = _detect_intervention_type(response_text)
    if intervention_type:
        details = _get_intervention_details(intervention_type, response_text)
        _send_intervention_alert(intervention_type, details)

        # Signal agent to pause and wait for resume directive
        # Returning a non-continue response pauses the agent loop
        print(json.dumps({
            "continue": False,
            "stopReason": f"intervention:{intervention_type}",
            "interventionType": intervention_type,
            "message": (
                f"Human intervention required: {intervention_type}. "
                "The current subtask is paused until Madpod sends a continuation instruction."
            ),
        }))
        return

    print(json.dumps({"continue": True}))


def _extract_domain(url: str) -> str | None:
    """Extract the domain from a URL, stripping www. prefix."""
    try:
        from urllib.parse import urlparse
        parsed = urlparse(url)
        hostname = parsed.hostname
        if hostname and hostname.startswith("www."):
            hostname = hostname[4:]
        return hostname
    except Exception:
        return None


def _send_auth_domain_warning(domain: str, url: str) -> None:
    """Send a proactive warning activity when navigating to an auth-required domain."""
    event = {
        "type": "activity",
        "agent_id": AGENT_ID,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "action": "status_update",
        "content": f"Navigating to {domain} which typically requires authentication. If a login wall appears, an intervention will be raised.",
        "tool_name": "browser_navigate",
    }
    channel_socket = os.environ.get("MADPOD_CHANNEL_SOCKET", f"/tmp/madpod-{AGENT_ID}.sock")
    try:
        import socket as sock_module
        with sock_module.socket(sock_module.AF_UNIX, sock_module.SOCK_STREAM) as s:
            s.settimeout(2.0)
            s.connect(channel_socket)
            s.sendall(json.dumps(event).encode() + b"\n")
    except Exception as e:
        logger.debug(f"Failed to send auth domain warning: {e}")


def _get_intervention_details(intervention_type: str, text: str) -> str:
    """Extract a concise description of what intervention is needed."""
    if intervention_type == "captcha":
        return "CAPTCHA challenge detected. Please solve it manually or use a CAPTCHA solver."
    elif intervention_type == "two_fa":
        return "Two-factor authentication required. Please provide the verification code."
    elif intervention_type == "login_wall":
        return "Login required. Please authenticate to continue."
    return f"Intervention required: {intervention_type}"


if __name__ == "__main__":
    raw = sys.stdin.read()
    process_hook_event(raw)
