# #region AuthSchemas [C:5] [TYPE Module] [SEMANTICS pydantic, auth, schema, token]
#
# @BRIEF Pydantic schemas for authentication requests and responses.
# @LAYER API
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
#
# @INVARIANT  Sensitive fields like password must not be included in response schemas.
# @DATA_CONTRACT  AuthPayload -> AuthSchema

from datetime import datetime
import re

from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator


# #region Token [C:1] [TYPE Class]
# @BRIEF Represents a JWT access token response.
class Token(BaseModel):
    access_token: str
    token_type: str


# #endregion Token


# #region TokenData [C:1] [TYPE Class]
# @BRIEF Represents the data encoded in a JWT token.
class TokenData(BaseModel):
    username: str | None = None
    scopes: list[str] = []


# #endregion TokenData


# #region PermissionSchema [C:1] [TYPE Class]
# @BRIEF Represents a permission in API responses.
class PermissionSchema(BaseModel):
    id: str | None = None
    resource: str
    action: str

    model_config = ConfigDict(from_attributes=True)


# #endregion PermissionSchema


# #region RoleSchema [TYPE Class]
# @BRIEF Represents a role in API responses.
class RoleSchema(BaseModel):
    id: str
    name: str
    description: str | None = None
    permissions: list[PermissionSchema] = []

    model_config = ConfigDict(from_attributes=True)


# #endregion RoleSchema


# #region RoleCreate [TYPE Class]
# @BRIEF Schema for creating a new role.
class RoleCreate(BaseModel):
    name: str
    description: str | None = None
    permissions: list[str] = []  # List of permission IDs or "resource:action" strings


# #endregion RoleCreate


# #region RoleUpdate [TYPE Class]
# @BRIEF Schema for updating an existing role.
class RoleUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    permissions: list[str] | None = None


# #endregion RoleUpdate


# #region ADGroupMappingSchema [TYPE Class]
# @BRIEF Represents an AD Group to Role mapping in API responses.
class ADGroupMappingSchema(BaseModel):
    id: str
    ad_group: str
    role_id: str

    model_config = ConfigDict(from_attributes=True)


# #endregion ADGroupMappingSchema


# #region ADGroupMappingCreate [TYPE Class]
# @BRIEF Schema for creating an AD Group mapping.
class ADGroupMappingCreate(BaseModel):
    ad_group: str
    role_id: str

    @field_validator("ad_group")
    @classmethod
    def validate_ad_group(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("AD group name must not be empty")
        # Allow DOMAIN\groupname or distinguishedName (CN=...,DC=...) formats
        if not re.match(r'^[A-Za-z0-9_.@()=\\\\-]+$', v):
            raise ValueError(
                "AD group name contains invalid characters. "
                "Use format: DOMAIN\\groupname or CN=groupname,DC=domain"
            )
        return v.strip()


# #endregion ADGroupMappingCreate


# #region UserBase [TYPE Class]
# @BRIEF Base schema for user data.
class UserBase(BaseModel):
    username: str
    email: EmailStr | None = None
    is_active: bool = True


# #endregion UserBase


# #region UserCreate [TYPE Class]
# @BRIEF Schema for creating a new user.
class UserCreate(UserBase):
    password: str = Field(min_length=8)
    roles: list[str] = []

    @field_validator("password")
    @classmethod
    def validate_password_strength(cls, v: str) -> str:
        if len(v) < 8:
            raise ValueError("Password must be at least 8 characters")
        if not any(c.isupper() for c in v):
            raise ValueError("Password must contain at least one uppercase letter")
        if not any(c.islower() for c in v):
            raise ValueError("Password must contain at least one lowercase letter")
        if not any(c.isdigit() for c in v):
            raise ValueError("Password must contain at least one digit")
        return v


# #endregion UserCreate


# #region UserUpdate [TYPE Class]
# @BRIEF Schema for updating an existing user.
class UserUpdate(BaseModel):
    email: EmailStr | None = None
    password: str | None = None
    is_active: bool | None = None
    roles: list[str] | None = None


# #endregion UserUpdate


# #region User [TYPE Class]
# @BRIEF Schema for user data in API responses.
class User(UserBase):
    id: str
    auth_source: str
    created_at: datetime
    last_login: datetime | None = None
    roles: list[RoleSchema] = []

    model_config = ConfigDict(from_attributes=True)


# #endregion User

# #endregion AuthSchemas
