"""
Input/output validation guardrails for agent execution.

This module provides validation functions to ensure agent inputs and outputs
meet security and quality requirements. It includes protection against
common injection attacks and size limits to prevent context overflow.

Requirements: AGENT-06 (input/output validation)
"""

import re
from typing import Any, Optional, List, Callable
from dataclasses import dataclass, field


class ValidationError(Exception):
    """Raised when validation fails."""

    pass


@dataclass
class ValidationRule:
    """A validation rule for input or output data.

    Rules can check for:
    - Maximum/minimum length
    - Required patterns
    - Forbidden patterns (e.g., injection attacks)
    """

    name: str
    pattern: Optional[str] = None
    max_length: Optional[int] = None
    min_length: Optional[int] = None
    forbidden_patterns: List[str] = field(default_factory=list)

    def __post_init__(self):
        """Initialize default values."""
        if self.forbidden_patterns is None:
            self.forbidden_patterns = []


# Default validation rules for inputs
DEFAULT_INPUT_RULES = [
    ValidationRule(
        name="no_sql_injection",
        forbidden_patterns=[
            r";\s*DROP\s+TABLE",
            r";\s*DELETE\s+FROM",
            r";\s*INSERT\s+INTO",
            r"--\s*$",
            r"'\s*OR\s+'",
            r"'\s*=\s*'",
        ],
    ),
    ValidationRule(
        name="no_command_injection",
        forbidden_patterns=[
            r";\s*rm\s+-rf",
            r"\|\s*rm\s+",
            r"`[^`]*\brm\b",
            r"\$\([^)]*\)",
            r";\s*sudo\s+",
        ],
    ),
    ValidationRule(
        name="no_script_injection",
        forbidden_patterns=[
            r"<script[^>]*>",
            r"javascript:",
            r"onerror\s*=",
            r"onload\s*=",
        ],
    ),
    ValidationRule(
        name="reasonable_length",
        max_length=100000,  # 100K chars max
    ),
]

# Default validation rules for outputs
DEFAULT_OUTPUT_RULES = [
    ValidationRule(
        name="output_length",
        max_length=1000000,  # 1M chars max
    ),
]


def validate_input(
    input_data: Any,
    rules: Optional[List[ValidationRule]] = None,
) -> bool:
    """Validate agent input against guardrails (AGENT-06).

    This function checks input data against a set of validation rules
    to prevent injection attacks and enforce size limits.

    Args:
        input_data: The input data to validate (will be converted to string)
        rules: Custom validation rules (uses DEFAULT_INPUT_RULES if None)

    Returns:
        True if validation passes

    Raises:
        ValidationError: If any validation rule fails

    Example:
        ```python
        from gsd_rlm.execution.validation import validate_input, ValidationError

        try:
            validate_input("Hello, world!")  # Passes
            validate_input("SELECT * FROM users; DROP TABLE users;--")  # Raises
        except ValidationError as e:
            print(f"Validation failed: {e}")
        ```
    """
    if rules is None:
        rules = DEFAULT_INPUT_RULES

    input_str = str(input_data) if input_data is not None else ""

    for rule in rules:
        # Check max length
        if rule.max_length and len(input_str) > rule.max_length:
            raise ValidationError(
                f"Input exceeds max length ({rule.max_length} chars): "
                f"got {len(input_str)} chars"
            )

        # Check min length
        if rule.min_length and len(input_str) < rule.min_length:
            raise ValidationError(
                f"Input below min length ({rule.min_length} chars): "
                f"got {len(input_str)} chars"
            )

        # Check required pattern
        if rule.pattern and not re.search(rule.pattern, input_str):
            raise ValidationError(
                f"Input does not match required pattern for rule '{rule.name}'"
            )

        # Check forbidden patterns
        for pattern in rule.forbidden_patterns:
            if re.search(pattern, input_str, re.IGNORECASE):
                raise ValidationError(
                    f"Input matches forbidden pattern '{rule.name}': {pattern}"
                )

    return True


def validate_output(
    output_data: Any,
    rules: Optional[List[ValidationRule]] = None,
) -> bool:
    """Validate agent output against guardrails (AGENT-06).

    This function checks output data against validation rules to
    enforce size limits and quality requirements.

    Args:
        output_data: The output data to validate (will be converted to string)
        rules: Custom validation rules (uses DEFAULT_OUTPUT_RULES if None)

    Returns:
        True if validation passes

    Raises:
        ValidationError: If any validation rule fails

    Example:
        ```python
        from gsd_rlm.execution.validation import validate_output

        # Validate agent output
        result = agent.process(input_message)
        validate_output(result)  # Raises ValidationError if too large
        ```
    """
    if rules is None:
        rules = DEFAULT_OUTPUT_RULES

    output_str = str(output_data) if output_data is not None else ""

    for rule in rules:
        # Check max length
        if rule.max_length and len(output_str) > rule.max_length:
            raise ValidationError(
                f"Output exceeds max length ({rule.max_length} chars): "
                f"got {len(output_str)} chars"
            )

        # Check min length
        if rule.min_length and len(output_str) < rule.min_length:
            raise ValidationError(
                f"Output below min length ({rule.min_length} chars): "
                f"got {len(output_str)} chars"
            )

    return True


def create_custom_validator(
    forbidden_patterns: List[str],
    max_length: int = 100000,
) -> Callable[[Any], bool]:
    """Create a custom validator function.

    This factory function creates a reusable validator with custom rules.

    Args:
        forbidden_patterns: List of regex patterns to forbid
        max_length: Maximum allowed length

    Returns:
        A validator function that raises ValidationError on failure

    Example:
        ```python
        from gsd_rlm.execution.validation import create_custom_validator

        # Create a validator for code input
        code_validator = create_custom_validator(
            forbidden_patterns=[r"exec\s*\(", r"eval\s*\("],
            max_length=50000
        )

        # Use the validator
        code_validator("print('hello')")  # Passes
        code_validator("exec('malicious')")  # Raises ValidationError
        ```
    """

    def validator(data: Any) -> bool:
        data_str = str(data) if data is not None else ""

        if len(data_str) > max_length:
            raise ValidationError(f"Data exceeds {max_length} chars")

        for pattern in forbidden_patterns:
            if re.search(pattern, data_str, re.IGNORECASE):
                raise ValidationError(f"Data matches forbidden pattern: {pattern}")

        return True

    return validator
