"""
Pydantic Validators and Custom Types
Reusable validators for Pydantic models across the application
"""
import re
from typing import Any, Optional, List
from pydantic import BaseModel, validator, root_validator, Field
from pydantic.functional_validators import AfterValidator
from typing_extensions import Annotated

from app.utils.validators import InputValidator
from app.utils.sanitizers import ContentSanitizer, ChatMessageSanitizer


# ============================================================
# CUSTOM ANNOTATED TYPES
# ============================================================

def validate_sanitized_string(v: str) -> str:
    """Sanitize string input"""
    if v is None:
        return v
    return InputValidator.sanitize_string(v)


def validate_email_field(v: str) -> str:
    """Validate and normalize email"""
    if v is None:
        return v
    return InputValidator.validate_email(v)


def validate_phone_field(v: str) -> str:
    """Validate and normalize phone"""
    if v is None:
        return v
    return InputValidator.validate_phone(v, required=False)


def validate_url_field(v: str) -> str:
    """Validate URL"""
    if v is None:
        return v
    return InputValidator.validate_url(v, required=False)


def validate_safe_string(v: str) -> str:
    """Validate string is safe from attacks"""
    if v is None:
        return v
    return InputValidator.validate_safe_input(v)


# Annotated types for use in Pydantic models
SanitizedString = Annotated[str, AfterValidator(validate_sanitized_string)]
EmailField = Annotated[str, AfterValidator(validate_email_field)]
PhoneField = Annotated[Optional[str], AfterValidator(validate_phone_field)]
UrlField = Annotated[Optional[str], AfterValidator(validate_url_field)]
SafeString = Annotated[str, AfterValidator(validate_safe_string)]


# ============================================================
# REUSABLE VALIDATORS
# ============================================================

class CommonValidators:
    """
    Collection of reusable Pydantic validators
    Use these as @validator decorators in your models
    """

    @staticmethod
    def sanitize_string(cls, v, field):
        """Basic string sanitization"""
        if v is None:
            return v
        return InputValidator.sanitize_string(str(v))

    @staticmethod
    def validate_email(cls, v):
        """Email validation"""
        if v is None:
            return v
        return InputValidator.validate_email(v)

    @staticmethod
    def validate_password(cls, v):
        """Password strength validation"""
        if v is None:
            return v
        return InputValidator.validate_password(v)

    @staticmethod
    def validate_phone(cls, v):
        """Phone number validation"""
        if v is None:
            return v
        return InputValidator.validate_phone(v, required=False)

    @staticmethod
    def validate_url(cls, v):
        """URL validation"""
        if v is None:
            return v
        return InputValidator.validate_url(v, required=False)

    @staticmethod
    def validate_positive_int(cls, v, field):
        """Validate positive integer"""
        if v is None:
            return v
        v = int(v)
        if v < 0:
            raise ValueError(f'{field.name} must be positive')
        return v

    @staticmethod
    def validate_percentage(cls, v, field):
        """Validate percentage (0-100)"""
        if v is None:
            return v
        v = float(v)
        if v < 0 or v > 100:
            raise ValueError(f'{field.name} must be between 0 and 100')
        return v

    @staticmethod
    def sanitize_filename(cls, v):
        """Filename sanitization"""
        if v is None:
            return v
        return InputValidator.sanitize_filename(v)

    @staticmethod
    def validate_safe_input(cls, v, field):
        """Check for SQL injection, XSS, etc."""
        if v is None:
            return v
        return InputValidator.validate_safe_input(str(v), field.name)

    @staticmethod
    def sanitize_chat_message(cls, v):
        """Sanitize chat message"""
        if v is None:
            return v
        return ChatMessageSanitizer.sanitize_message(v)

    @staticmethod
    def validate_json_data(cls, v):
        """Validate and sanitize JSON data"""
        if v is None:
            return v
        return ContentSanitizer.sanitize_json_string(v)


# ============================================================
# BASE MODELS WITH BUILT-IN VALIDATION
# ============================================================

class SanitizedBaseModel(BaseModel):
    """
    Base model that automatically sanitizes string fields
    """

    class Config:
        # Strip whitespace from strings
        str_strip_whitespace = True
        # Validate on assignment
        validate_assignment = True

    @root_validator(pre=True)
    def sanitize_all_strings(cls, values):
        """Sanitize all string values"""
        sanitized = {}
        for key, value in values.items():
            if isinstance(value, str):
                # Basic sanitization
                value = InputValidator.sanitize_string(value)
            sanitized[key] = value
        return sanitized


class SecureInputModel(BaseModel):
    """
    Base model with security checks on all inputs
    """

    class Config:
        str_strip_whitespace = True
        validate_assignment = True

    @root_validator(pre=True)
    def check_security(cls, values):
        """Check all string values for security issues"""
        for key, value in values.items():
            if isinstance(value, str):
                # Check for attacks
                if InputValidator.detect_sql_injection(value):
                    raise ValueError(f"Invalid input in {key}")
                if InputValidator.detect_xss(value):
                    raise ValueError(f"Invalid input in {key}")
                # Sanitize
                values[key] = InputValidator.sanitize_string(value)
        return values


# ============================================================
# COMMON FIELD DEFINITIONS
# ============================================================

class FieldDefinitions:
    """
    Pre-defined field configurations for common use cases
    """

    @staticmethod
    def name_field(min_length: int = 1, max_length: int = 100, required: bool = True):
        """Name field with validation"""
        return Field(
            ...,
            min_length=min_length,
            max_length=max_length,
            description="Name field"
        ) if required else Field(
            None,
            min_length=min_length,
            max_length=max_length,
            description="Name field"
        )

    @staticmethod
    def email_field(required: bool = True):
        """Email field with validation"""
        return Field(
            ...,
            max_length=254,
            description="Email address"
        ) if required else Field(
            None,
            max_length=254,
            description="Email address"
        )

    @staticmethod
    def password_field():
        """Password field with validation"""
        return Field(
            ...,
            min_length=8,
            max_length=128,
            description="Password (min 8 characters)"
        )

    @staticmethod
    def phone_field():
        """Phone field"""
        return Field(
            None,
            max_length=20,
            description="Phone number in E.164 format"
        )

    @staticmethod
    def url_field():
        """URL field"""
        return Field(
            None,
            max_length=2048,
            description="Valid HTTP(S) URL"
        )

    @staticmethod
    def text_field(max_length: int = 1000, required: bool = False):
        """Text area field"""
        return Field(
            ...,
            max_length=max_length,
            description="Text content"
        ) if required else Field(
            None,
            max_length=max_length,
            description="Text content"
        )

    @staticmethod
    def id_field():
        """ID field (positive integer)"""
        return Field(..., gt=0, description="Resource ID")

    @staticmethod
    def pagination_page():
        """Page number field"""
        return Field(1, ge=1, le=10000, description="Page number")

    @staticmethod
    def pagination_limit():
        """Page size field"""
        return Field(20, ge=1, le=100, description="Items per page")


# ============================================================
# EXAMPLE VALIDATED MODELS
# ============================================================

class UserRegistrationRequest(SecureInputModel):
    """Example: User registration with full validation"""
    email: str = FieldDefinitions.email_field()
    password: str = FieldDefinitions.password_field()
    first_name: str = FieldDefinitions.name_field(min_length=2, max_length=50)
    last_name: Optional[str] = FieldDefinitions.name_field(min_length=2, max_length=50, required=False)
    phone: Optional[str] = FieldDefinitions.phone_field()
    company_name: Optional[str] = FieldDefinitions.name_field(max_length=200, required=False)

    @validator('email')
    def validate_email(cls, v):
        return InputValidator.validate_email(v)

    @validator('password')
    def validate_password(cls, v):
        return InputValidator.validate_password(v)

    @validator('phone')
    def validate_phone(cls, v):
        if v:
            return InputValidator.validate_phone(v, required=False)
        return v

    @validator('first_name', 'last_name', 'company_name')
    def validate_names(cls, v, field):
        if v:
            return InputValidator.validate_name(v, field.name, required=False)
        return v


class ChatMessageRequest(SanitizedBaseModel):
    """Example: Chat message with sanitization"""
    message: str = Field(..., min_length=1, max_length=4000)
    session_id: Optional[str] = Field(None, max_length=100)

    @validator('message')
    def sanitize_message(cls, v):
        # Sanitize
        v = ChatMessageSanitizer.sanitize_message(v)

        # Optionally check for prompt injection
        if ChatMessageSanitizer.detect_prompt_injection(v):
            # Log but don't block - let the AI handle it
            import logging
            logging.warning("Potential prompt injection in message")

        return v

    @validator('session_id')
    def validate_session_id(cls, v):
        if v:
            # Only alphanumeric, underscore, hyphen
            if not re.match(r'^[a-zA-Z0-9_-]+$', v):
                raise ValueError('Invalid session ID format')
        return v


class WebhookConfigRequest(SecureInputModel):
    """Example: Webhook configuration with URL validation"""
    name: str = FieldDefinitions.name_field(max_length=100)
    url: str = Field(..., max_length=500)
    events: List[str] = Field(..., min_items=1, max_items=20)
    secret: Optional[str] = Field(None, max_length=64)

    @validator('url')
    def validate_url(cls, v):
        return InputValidator.validate_url(v, required=True, allowed_schemes=['https'])

    @validator('events')
    def validate_events(cls, v):
        # Sanitize each event name
        return [InputValidator.sanitize_string(e, max_length=50) for e in v]
