"""
Regex Pattern Constants for Family Safety Agent

This module centralizes all regex patterns used throughout the application.
Extracting regex patterns to constants improves maintainability, readability,
and allows for easier testing and reuse.
"""

import re
from typing import Optional, Match

# ============================================================================
# WAIT PATTERNS
# ============================================================================

# Match wait instructions: "wait for 5 seconds", "wait 5s", "wait 2.5 seconds"
# Groups: (1) numeric value, (2) optional time unit
PATTERN_WAIT_TIME = r"wait(?:\s+for)?\s+(\d+(?:\.\d+)?)\s*(s|sec|secs|second|seconds)?\b"


# ============================================================================
# CLICK PATTERNS
# ============================================================================

# Match card click: "click on 'user' card", "click the 'John' card"
# Group (1): card name/identifier
PATTERN_CLICK_CARD = r"\bclick(?:\s+(?:in|on|the))?\s+['\"]([^'\"]+)['\"]\s+card\b"

# Match app click: "click YouTube app from the search results"
# Group (1): app name
PATTERN_CLICK_APP = r"click\s+(?:on\s+)?(?:the\s+)?([^'\"]+?)\s+app\s+(?:from|in)\s+(?:the\s+)?(?:search results|list|apps list|apps store|add a tab)"

# Match first app/result click: "click the first app from the list"
# Group (1): context (search results, list, etc.)
PATTERN_CLICK_FIRST_APP = r"click\s+(?:on\s+)?(?:the\s+)?first\s+(?:app|result)\s+(?:from|in)\s+(?:the\s+)?(search results|list|apps list|apps store|add a tab)"

# Match double click: "double click on 'text'", "double click on element text"
# Groups: (1) target text, (2) optional " text" suffix
PATTERN_DOUBLE_CLICK = r"\bdouble\s+click\s+on\s+['\"]?(.+?)['\"]?(\s+text\b)?"


# ============================================================================
# TEXT EXTRACTION PATTERNS
# ============================================================================

# Match text within quotes (single or double)
# Groups: (1) single-quoted text OR (2) double-quoted text
PATTERN_QUOTED_TEXT = r"'([^']+)'|\"([^\"]+)\""

# Match text within double quotes only
# Group (1): double-quoted text
PATTERN_QUOTE_DOUBLE = r"['\"]([^'\"]+)['\"]"


# ============================================================================
# KEYBOARD PATTERNS
# ============================================================================

# Match keyboard press: "press Ctrl+S", "press Tab 3 times", "press Enter"
# Groups: (1) key combination, (2) optional repeat count
PATTERN_KEYBOARD_PRESS = r"press\s+([a-zA-Z+]+)(?:\s+(\d+)\s*(?:x|times)?)?"


# ============================================================================
# ACCOUNT & FAMILY PATTERNS
# ============================================================================

# Match login with account: "login with "account name" account"
# Group (1): account name
PATTERN_LOGIN_ACCOUNT = r'^login\s+with\s+"([^"]+)"\s+account$'

# Match switch family member: "switch "member name" family member"
# Group (1): family member name
PATTERN_SWITCH_FAMILY = r'^switch\s+"([^"]+)"\s+family\s+member$'


# ============================================================================
# CONTEXT PATTERNS
# ============================================================================

# Match search results context
# Group (1): search results or results
PATTERN_SEARCH_RESULTS = r"\b(search\s*results|results)\b"

# Match windows keyword: "windows settings", "windows store"
# Group (1): windows context keywords
PATTERN_WINDOWS_CONTEXT = r"windows\s+(\w+(?:\s+\w+)*)"


# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

def extract_wait_time(step: str) -> Optional[float]:
    """
    Extract wait time in seconds from a step string.
    
    Args:
        step: The step string to parse
        
    Returns:
        Wait time in seconds, or None if no wait pattern found
        
    Example:
        extract_wait_time("wait for 5 seconds") -> 5.0
        extract_wait_time("wait 2.5s") -> 2.5
    """
    match = re.search(PATTERN_WAIT_TIME, step.lower())
    if match:
        try:
            secs = float(match.group(1))
            return secs if secs > 0 else None
        except (ValueError, AttributeError):
            return None
    return None


def extract_card_name(step: str) -> Optional[str]:
    """
    Extract card name from a click card command.
    
    Args:
        step: The step string to parse
        
    Returns:
        Card name/identifier, or None if no match
        
    Example:
        extract_card_name("click on 'John Doe' card") -> "John Doe"
    """
    match = re.search(PATTERN_CLICK_CARD, step, re.IGNORECASE)
    return match.group(1).strip() if match else None


def extract_app_name(step: str) -> Optional[str]:
    """
    Extract app name from a click app command.
    
    Args:
        step: The step string to parse
        
    Returns:
        App name, or None if no match
        
    Example:
        extract_app_name("click YouTube app from the search results") -> "YouTube"
    """
    match = re.search(PATTERN_CLICK_APP, step.lower())
    if match:
        app_name = match.group(1).strip().strip('.')
        # Normalize common filler words
        app_name = re.sub(r"\b(app|from|list|search results)\b", "", app_name, flags=re.IGNORECASE).strip()
        return app_name if app_name else None
    return None


def is_first_app_click(step: str) -> bool:
    """
    Check if step is requesting to click the first app/result.
    
    Args:
        step: The step string to parse
        
    Returns:
        True if step matches first app click pattern
        
    Example:
        is_first_app_click("click the first app from the list") -> True
    """
    return bool(re.search(PATTERN_CLICK_FIRST_APP, step.lower()))


def extract_keyboard_press(step: str) -> Optional[tuple[str, int]]:
    """
    Extract keyboard key and repeat count from a press command.
    
    Args:
        step: The step string to parse
        
    Returns:
        Tuple of (key, count) or None if no match
        
    Example:
        extract_keyboard_press("press Tab 3 times") -> ("Tab", 3)
        extract_keyboard_press("press Ctrl+S") -> ("Ctrl+S", 1)
    """
    match = re.search(PATTERN_KEYBOARD_PRESS, step.lower())
    if match:
        key = match.group(1)
        count = int(match.group(2)) if match.group(2) else 1
        return (key, count) if count > 0 else None
    return None


def extract_account_name(step: str) -> Optional[str]:
    """
    Extract account name from login command.
    
    Args:
        step: The step string to parse
        
    Returns:
        Account name, or None if no match
        
    Example:
        extract_account_name('login with "john@example.com" account') -> "john@example.com"
    """
    match = re.match(PATTERN_LOGIN_ACCOUNT, step.lower())
    return match.group(1).strip() if match else None


def extract_family_member(step: str) -> Optional[str]:
    """
    Extract family member name from switch command.
    
    Args:
        step: The step string to parse
        
    Returns:
        Family member name, or None if no match
        
    Example:
        extract_family_member('switch "Child Account" family member') -> "Child Account"
    """
    match = re.match(PATTERN_SWITCH_FAMILY, step.lower(), re.IGNORECASE)
    return match.group(1).strip() if match else None


def extract_double_click_target(step: str) -> Optional[tuple[str, bool]]:
    """
    Extract target text from double click command.
    
    Args:
        step: The step string to parse
        
    Returns:
        Tuple of (target_text, force_text_span) or None if no match
        
    Example:
        extract_double_click_target("double click on 'Save' text") -> ("Save", True)
        extract_double_click_target("double click on element") -> ("element", False)
    """
    match = re.search(PATTERN_DOUBLE_CLICK, step.lower(), re.IGNORECASE)
    if match:
        target = match.group(1)
        force_text_span = bool(match.group(2))
        return (target, force_text_span)
    return None


def extract_quoted_text(step: str) -> Optional[str]:
    """
    Extract text within quotes (single or double).
    
    Args:
        step: The step string to parse
        
    Returns:
        Quoted text, or None if no match
        
    Example:
        extract_quoted_text("click on 'button'") -> "button"
        extract_quoted_text('search for "YouTube"') -> "YouTube"
    """
    match = re.search(PATTERN_QUOTED_TEXT, step)
    if match:
        # Return whichever group matched (single or double quotes)
        return match.group(1) if match.group(1) else match.group(2)
    return None


def has_search_results_context(step: str) -> bool:
    """
    Check if step mentions search results context.
    
    Args:
        step: The step string to parse
        
    Returns:
        True if step contains search results reference
        
    Example:
        has_search_results_context("click item from search results") -> True
    """
    return bool(re.search(PATTERN_SEARCH_RESULTS, step.lower()))


def extract_windows_context(step: str) -> Optional[str]:
    """
    Extract Windows-specific context keywords.
    
    Args:
        step: The step string to parse
        
    Returns:
        Windows context keywords, or None if no match
        
    Example:
        extract_windows_context("navigate to windows settings") -> "settings"
    """
    match = re.search(PATTERN_WINDOWS_CONTEXT, step.lower())
    return match.group(1) if match else None
