from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
from typing import Optional
from datetime import datetime
 
import re
from typing import Any
    
    

class UserBase(BaseModel):
    """Base user schema."""
    username: str
    email: EmailStr
    full_name: Optional[str] = None


class UserCreate(UserBase):
    """User creation schema."""
    password: str
    
    @field_validator('password')
    @classmethod
    def validate_password(cls, v: str) -> str:
        """Validate password meets security requirements."""
        # Check byte length (bcrypt limit with safety margin)
        if len(v.encode('utf-8')) > 72:
            raise ValueError(
                'Password must be 72 bytes or less (approximately 72 characters). '
                'Try a shorter password.'
            )
        
        # Minimum length
        if len(v) < 8:
            raise ValueError('Password must be at least 8 characters long')
        
        # Require uppercase
        if not any(c.isupper() for c in v):
            raise ValueError('Password must contain at least one uppercase letter')
        
        # Require lowercase
        if not any(c.islower() for c in v):
            raise ValueError('Password must contain at least one lowercase letter')
        
        # Require digit
        if not any(c.isdigit() for c in v):
            raise ValueError('Password must contain at least one digit')
        
        # Require special character (optional but recommended)
        special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
        if not any(c in special_chars for c in v):
            raise ValueError(
                'Password must contain at least one special character (!@#$%^&*()_+-=[]{}|;:,.<>?)'
            )
        
        return v
    
    @field_validator('username')
    @classmethod
    def validate_username(cls, v: str) -> str:
        """Validate username format."""
        if len(v) < 3:
            raise ValueError('Username must be at least 3 characters long')
        
        if len(v) > 50:
            raise ValueError('Username must be 50 characters or less')
        
        # Only allow alphanumeric and underscore
        if not v.replace('_', '').replace('-', '').isalnum():
            raise ValueError('Username can only contain letters, numbers, hyphens, and underscores')
        
        return v


class UserUpdate(BaseModel):
    """User update schema."""
    full_name: Optional[str] = None
    email: Optional[EmailStr] = None


class UserResponse(UserBase):
    """User response schema."""
    id: int
    is_active: bool
    is_superuser: bool
    mfa_enabled: bool
    created_at: datetime
    last_login: Optional[datetime] = None
    
    model_config = ConfigDict(from_attributes=True, protected_namespaces=())


class UserProfile(UserResponse):
    """Extended user profile."""
    total_models: int = 0


class PasswordChange(BaseModel):
    """Password change request."""
    old_password: str
    new_password: str = Field(..., min_length=8, max_length=72)
    
    @field_validator('new_password')
    @classmethod
    def validate_new_password(cls, v: str) -> str:
        """Validate new password meets security requirements."""
        # Check byte length
        if len(v.encode('utf-8')) > 72:
            raise ValueError('Password must be 72 bytes or less')
        
        # Minimum length
        if len(v) < 8:
            raise ValueError('Password must be at least 8 characters long')
        
        # Require uppercase
        if not any(c.isupper() for c in v):
            raise ValueError('Password must contain at least one uppercase letter')
        
        # Require lowercase
        if not any(c.islower() for c in v):
            raise ValueError('Password must contain at least one lowercase letter')
        
        # Require digit
        if not any(c.isdigit() for c in v):
            raise ValueError('Password must contain at least one digit')
        
        # Require special character
        special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
        if not any(c in special_chars for c in v):
            raise ValueError(
                'Password must contain at least one special character (!@#$%^&*()_+-=[]{}|;:,.<>?)'
            )
        
        return v
   

    def validate_email(email: str) -> bool:
        """Validate email format."""
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        return bool(re.match(pattern, email))


    def validate_password_strength(password: str) -> tuple[bool, str]:
        """
        Validate password strength.

        Requirements:
        - At least 8 characters
        - Contains uppercase and lowercase letters
        - Contains at least one number
        - Contains at least one special character
        """
        if len(password) < 8:
            return False, "Password must be at least 8 characters long"

        if not re.search(r'[A-Z]', password):
            return False, "Password must contain at least one uppercase letter"

        if not re.search(r'[a-z]', password):
            return False, "Password must contain at least one lowercase letter"

        if not re.search(r'\d', password):
            return False, "Password must contain at least one number"

        if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
            return False, "Password must contain at least one special character"

        return True, "Password is valid"


    def validate_model_name(name: str) -> bool:
        """Validate model name (alphanumeric, hyphens, underscores only)."""
        pattern = r'^[a-zA-Z0-9_-]+$'
        return bool(re.match(pattern, name))


    def validate_file_extension(filename: str, allowed_extensions: list[str]) -> bool:
        """Check if file has an allowed extension."""
        return any(filename.lower().endswith(ext) for ext in allowed_extensions)


    def sanitize_filename(filename: str) -> str:
        """Sanitize filename by removing potentially dangerous characters."""
        # Remove path traversal attempts
        filename = filename.replace('..', '').replace('/', '').replace('\\', '')
        # Keep only alphanumeric, dots, hyphens, underscores
        filename = re.sub(r'[^a-zA-Z0-9._-]', '_', filename)
        return filename

