"""
Pydantic Validation Utilities for groknroll.

Provides data validation using Pydantic v2 for type safety and clear error messages.

Example:
    from groknroll.core.validation import ToolParams, validate_tool_params

    # Define tool parameters
    class ReadToolParams(ToolParams):
        path: str
        offset: int = 0
        limit: Optional[int] = None

        @field_validator('offset')
        def offset_non_negative(cls, v):
            if v < 0:
                raise ValueError('offset must be non-negative')
            return v

    # Validate parameters
    params = validate_tool_params(ReadToolParams, raw_params)
"""

from typing import Any, Dict, Optional, Type, TypeVar

from pydantic import BaseModel, ConfigDict, Field, field_validator


T = TypeVar("T", bound=BaseModel)


class ToolParams(BaseModel):
    """
    Base class for tool parameter validation.

    All tool parameter models should inherit from this.
    """

    model_config = ConfigDict(
        extra="forbid",  # Reject unknown fields
        str_strip_whitespace=True,  # Strip whitespace from strings
        validate_assignment=True,  # Validate on attribute assignment
    )


class FileOperationParams(ToolParams):
    """Parameters for file operations (read, write, edit)."""

    path: str = Field(..., description="File path")
    offset: int = Field(default=0, ge=0, description="Line offset (0-indexed)")
    limit: Optional[int] = Field(default=None, ge=1, description="Number of lines to read")


class SearchParams(ToolParams):
    """Parameters for search operations (grep, glob)."""

    pattern: str = Field(..., min_length=1, description="Search pattern")
    path: Optional[str] = Field(default=None, description="Path to search in")
    case_insensitive: bool = Field(default=False, description="Case-insensitive search")


class BashParams(ToolParams):
    """Parameters for bash command execution."""

    command: str = Field(..., min_length=1, description="Bash command to execute")
    timeout: int = Field(default=120, ge=1, le=600, description="Timeout in seconds")
    cwd: Optional[str] = Field(default=None, description="Working directory")


class GitParams(ToolParams):
    """Parameters for git operations."""

    operation: str = Field(..., description="Git operation (add, commit, push, etc.)")
    files: Optional[list[str]] = Field(default=None, description="Files to operate on")
    message: Optional[str] = Field(default=None, description="Commit message")
    remote: str = Field(default="origin", description="Git remote name")
    branch: Optional[str] = Field(default=None, description="Git branch name")


class RLMConfig(BaseModel):
    """RLM configuration validation."""

    model_config = ConfigDict(extra="allow")  # Allow provider-specific fields

    backend: str = Field(..., description="LLM backend (openai, anthropic, etc.)")
    model_name: str = Field(..., description="Model name")
    max_iterations: int = Field(default=30, ge=1, le=100, description="Max RLM iterations")
    max_depth: int = Field(default=1, ge=0, le=3, description="Max recursion depth")
    max_cost: Optional[float] = Field(default=None, ge=0, description="Max cost in USD")
    timeout_seconds: Optional[float] = Field(
        default=None, ge=1, description="Total timeout in seconds"
    )

    @field_validator("max_cost")
    def validate_max_cost(cls, v):
        """Validate max_cost is reasonable."""
        if v is not None and v > 1000:
            raise ValueError("max_cost seems unreasonably high (>$1000)")
        return v


class PermissionConfig(BaseModel):
    """Permission configuration validation."""

    model_config = ConfigDict(extra="forbid")

    mode: str = Field(..., pattern="^(allow|ask|deny)$", description="Permission mode")
    paths: Optional[list[str]] = Field(default=None, description="Allowed/denied paths")
    commands: Optional[list[str]] = Field(default=None, description="Allowed/denied commands")


class MCPServerConfig(BaseModel):
    """MCP server configuration validation."""

    model_config = ConfigDict(extra="forbid")

    command: str = Field(..., min_length=1, description="Server command")
    args: list[str] = Field(default_factory=list, description="Command arguments")
    env: Optional[Dict[str, str]] = Field(default=None, description="Environment variables")
    transport: str = Field(
        default="stdio",
        pattern="^(stdio|sse|http)$",
        description="Transport protocol",
    )


class LSPServerConfig(BaseModel):
    """LSP server configuration validation."""

    model_config = ConfigDict(extra="forbid")

    command: str = Field(..., min_length=1, description="Server command")
    args: list[str] = Field(default_factory=list, description="Command arguments")
    language: str = Field(..., description="Programming language")
    file_extensions: list[str] = Field(
        ..., min_length=1, description="File extensions (e.g., ['.py'])"
    )


def validate_tool_params(param_class: Type[T], params: Dict[str, Any]) -> T:
    """
    Validate tool parameters using Pydantic model.

    Args:
        param_class: Pydantic model class for validation
        params: Raw parameter dictionary

    Returns:
        Validated parameter object

    Raises:
        ValidationError: If parameters are invalid

    Example:
        params = validate_tool_params(FileOperationParams, {"path": "/tmp/test.txt"})
    """
    return param_class(**params)


def validate_config(config_class: Type[T], config: Dict[str, Any]) -> T:
    """
    Validate configuration using Pydantic model.

    Args:
        config_class: Pydantic model class for validation
        config: Raw configuration dictionary

    Returns:
        Validated configuration object

    Raises:
        ValidationError: If configuration is invalid

    Example:
        config = validate_config(RLMConfig, raw_config)
    """
    return config_class(**config)


def safe_validate(param_class: Type[T], params: Dict[str, Any]) -> tuple[Optional[T], Optional[str]]:
    """
    Safely validate parameters without raising exceptions.

    Args:
        param_class: Pydantic model class for validation
        params: Raw parameter dictionary

    Returns:
        Tuple of (validated_params, error_message)
        If validation succeeds: (params, None)
        If validation fails: (None, error_message)

    Example:
        params, error = safe_validate(FileOperationParams, raw_params)
        if error:
            print(f"Validation error: {error}")
    """
    try:
        validated = param_class(**params)
        return validated, None
    except Exception as e:
        return None, str(e)
