from typing import Optional, List, Dict, Any, Annotated
from pydantic import BaseModel, Field, EmailStr, field_validator, model_validator, ConfigDict, BeforeValidator
from datetime import datetime
from utils.utils import UserRole

StrId = Annotated[str, BeforeValidator(str)]


# User schemas
class UserBase(BaseModel):
    email: EmailStr
    first_name: str = Field(..., max_length=100)
    last_name: str = Field(..., max_length=100)
    role: UserRole
    phone: Optional[str] = Field(None, max_length=20)
    department_id: Optional[str] = Field(None, max_length=36)
    is_active: bool = True
    
    @field_validator("email")
    @classmethod
    def normalise_email(cls, v):
        if v is None:
            return v
        return v.strip().lower()
    
    @field_validator("first_name", "last_name")
    @classmethod
    def strip_names(cls, v):
        if v is None:
            return v
        return v.strip()


class UserCreate(UserBase):
    password: str = Field(..., min_length=8)
    
    @field_validator("password")
    @classmethod
    def validate_password_complexity(cls, v):
        if v is None:
            return v
        if len(v) < 8:
            raise ValueError("Password must be at least 8 characters long")
        if not any(c.isalpha() for c in v):
            raise ValueError("Password must contain at least one letter")
        if not any(c.isdigit() for c in v):
            raise ValueError("Password must contain at least one digit")
        return v


class UserUpdate(BaseModel):
    email: Optional[EmailStr] = None
    first_name: Optional[str] = Field(None, max_length=100)
    last_name: Optional[str] = Field(None, max_length=100)
    role: Optional[UserRole] = None
    phone: Optional[str] = Field(None, max_length=20)
    department_id: Optional[str] = Field(None, max_length=36)
    is_active: Optional[bool] = None
    
    @field_validator("email")
    @classmethod
    def normalise_email(cls, v):
        if v is None:
            return v
        return v.strip().lower()
    
    @field_validator("first_name", "last_name")
    @classmethod
    def strip_names(cls, v):
        if v is None:
            return v
        return v.strip()


class UserResponse(BaseModel):
    id: StrId
    email: str
    first_name: str
    last_name: str
    role: UserRole
    phone: Optional[str]
    department_id: Optional[StrId]
    is_active: bool
    created_at: datetime
    updated_at: datetime
    
    model_config = ConfigDict(from_attributes=True)


class UserDetailsResponse(BaseModel):
    id: StrId
    email: str
    first_name: str
    last_name: str
    role: UserRole
    phone: Optional[str]
    department_id: Optional[StrId]
    is_active: bool
    created_at: datetime
    updated_at: datetime
    department_name: Optional[str]
    
    model_config = ConfigDict(from_attributes=True)


# Auditlog schemas
class AuditlogBase(BaseModel):
    user_id: Optional[str] = Field(None, max_length=36)
    action: str = Field(..., max_length=100)
    entity_type: str = Field(..., max_length=100)
    entity_id: Optional[str] = Field(None, max_length=36)
    old_values: Optional[Dict[str, Any]] = None
    new_values: Optional[Dict[str, Any]] = None
    ip_address: Optional[str] = Field(None, max_length=45)
    user_agent: Optional[str] = None


class AuditlogCreate(AuditlogBase):
    pass


class AuditlogUpdate(BaseModel):
    user_id: Optional[str] = Field(None, max_length=36)
    action: Optional[str] = Field(None, max_length=100)
    entity_type: Optional[str] = Field(None, max_length=100)
    entity_id: Optional[str] = Field(None, max_length=36)
    old_values: Optional[Dict[str, Any]] = None
    new_values: Optional[Dict[str, Any]] = None
    ip_address: Optional[str] = Field(None, max_length=45)
    user_agent: Optional[str] = None


class AuditlogResponse(BaseModel):
    id: StrId
    user_id: Optional[StrId]
    action: str
    entity_type: str
    entity_id: Optional[str]
    old_values: Optional[Dict[str, Any]]
    new_values: Optional[Dict[str, Any]]
    ip_address: Optional[str]
    user_agent: Optional[str]
    created_at: datetime
    updated_at: datetime
    
    model_config = ConfigDict(from_attributes=True)


# Paginated response wrappers
from typing import Generic, TypeVar

T = TypeVar("T")


class PaginatedResponse(BaseModel, Generic[T]):
    items: List[T]
    total: int
    limit: int
    offset: int
    
    model_config = ConfigDict(from_attributes=True)