"""Enhanced validation utilities with constraint checking."""

import re
from typing import List, Tuple, Optional, Any
from ..constants import MARKDOWN_EXTENSIONS, ERROR_MESSAGES


class ValidationError(ValueError):
    """Custom validation error with detailed messages."""
    pass


def validate_note_path(path: str) -> Tuple[bool, Optional[str]]:
    """
    Validate a note path with comprehensive checks.
    
    Args:
        path: Path to validate
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if not path:
        return False, "Path cannot be empty"
    
    # Check length (matching our schema constraint)
    if len(path) > 255:
        return False, ERROR_MESSAGES["path_too_long"].format(length=len(path))
    
    # Check pattern (must not start with /)
    if path.startswith("/"):
        return False, ERROR_MESSAGES["invalid_path"].format(path=path)
    
    # Check for path traversal attempts
    if ".." in path:
        return False, ERROR_MESSAGES["invalid_path"].format(path=path)
    
    # Check extension
    if not any(path.endswith(ext) for ext in MARKDOWN_EXTENSIONS):
        return False, ERROR_MESSAGES["invalid_path"].format(path=path)
    
    # Check for invalid characters (Windows-specific restrictions)
    # Note: We allow quotes since they're valid on macOS/Linux
    invalid_chars = ["<", ">", ":", "|", "?", "*"]
    for char in invalid_chars:
        if char in path:
            return False, ERROR_MESSAGES["invalid_path"].format(path=path)
    
    # Check pattern matches our schema
    pattern = r"^[^/].*\.md$"
    if not re.match(pattern, path):
        return False, ERROR_MESSAGES["invalid_path"].format(path=path)
    
    return True, None


def validate_search_query(query: str) -> Tuple[bool, Optional[str]]:
    """
    Validate a search query.
    
    Args:
        query: Search query to validate
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if not query or not query.strip():
        return False, ERROR_MESSAGES["empty_search_query"]
    
    # Check length constraint
    if len(query) > 500:
        return False, f"Search query too long: {len(query)} characters (max: 500)"
    
    return True, None


def validate_context_length(length: int) -> Tuple[bool, Optional[str]]:
    """
    Validate context length parameter.
    
    Args:
        length: Context length to validate
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if length < 10 or length > 500:
        return False, ERROR_MESSAGES["invalid_context_length"].format(length=length)
    
    return True, None


def validate_date_search_params(
    date_type: str, 
    days_ago: int, 
    operator: str
) -> Tuple[bool, Optional[str]]:
    """
    Validate date search parameters.
    
    Args:
        date_type: Type of date to search by
        days_ago: Number of days to look back
        operator: Search operator
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    # Validate date_type
    if date_type not in ["created", "modified"]:
        return False, ERROR_MESSAGES["invalid_date_type"].format(date_type=date_type)
    
    # Validate operator
    if operator not in ["within", "exactly"]:
        return False, ERROR_MESSAGES["invalid_operator"].format(operator=operator)
    
    # Validate days_ago
    if days_ago < 0:
        return False, ERROR_MESSAGES["negative_days"].format(days=days_ago)
    
    if days_ago > 365:
        return False, f"Days ago too large: {days_ago} (max: 365). Use smaller values or implement year-based search."
    
    return True, None


def validate_tags(tags: List[str]) -> Tuple[bool, Optional[str]]:
    """
    Validate a list of tags.
    
    Args:
        tags: List of tags to validate
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if not tags:
        return False, ERROR_MESSAGES["invalid_tags"]
    
    # Check each tag
    cleaned_tags = []
    for tag in tags:
        # Remove # prefix and strip whitespace
        cleaned = tag.lstrip("#").strip()
        if not cleaned:
            return False, ERROR_MESSAGES["invalid_tags"]
        cleaned_tags.append(cleaned)
    
    # Check count constraints
    if len(cleaned_tags) > 50:
        return False, f"Too many tags: {len(cleaned_tags)} (max: 50). Consider organizing with folders instead."
    
    return True, None


def validate_directory_path(path: Optional[str]) -> Tuple[bool, Optional[str]]:
    """
    Validate a directory path for listing.
    
    Args:
        path: Directory path to validate (can be None)
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if path is None:
        return True, None
    
    # Check length
    if len(path) > 255:
        return False, f"Directory path too long: {len(path)} characters (max: 255)"
    
    # Check pattern (must not start or end with /)
    if path.startswith("/"):
        return False, f"Directory path cannot start with '/': {path}"
    
    if path.endswith("/"):
        return False, f"Directory path cannot end with '/': {path}"
    
    # Check for path traversal
    if ".." in path:
        return False, f"Directory path cannot contain '..': {path}"
    
    return True, None


def validate_content(content: str) -> Tuple[bool, Optional[str]]:
    """
    Validate note content.
    
    Args:
        content: Note content to validate
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    # Check size constraint (1MB limit)
    if len(content) > 1_000_000:
        return False, f"Content too large: {len(content)} characters (max: 1,000,000). Consider splitting into multiple notes."
    
    return True, None


# Validation decorators for use in tools
def validate_params(validation_func):
    """Decorator to validate parameters before calling a function."""
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # Run validation
            is_valid, error = validation_func(*args, **kwargs)
            if not is_valid:
                raise ValidationError(error)
            
            # Call the original function
            return await func(*args, **kwargs)
        return wrapper
    return decorator