from datetime import datetime from typing import Any, Literal from uuid import UUID from pydantic import BaseModel, EmailStr, Field, field_validator class RegisterRequest(BaseModel): email: EmailStr password: str = Field(min_length=8, max_length=128) class LoginRequest(BaseModel): email: EmailStr password: str = Field(min_length=1, max_length=128) class PasswordChangeRequest(BaseModel): current_password: str = Field(min_length=1, max_length=128) new_password: str = Field(min_length=8, max_length=128) class UserPublic(BaseModel): id: UUID email: str | None phone: str | None role: Literal["user", "admin"] plan: Literal["free", "vip"] status: Literal["active", "disabled"] email_verified: bool phone_verified: bool created_at: datetime last_login_at: datetime | None class AuthResponse(BaseModel): user: UserPublic class MembershipRequest(BaseModel): plan: Literal["free", "vip"] starts_at: datetime | None = None expires_at: datetime | None = None reason: str = Field(min_length=1, max_length=500) class StatusRequest(BaseModel): status: Literal["active", "disabled"] reason: str = Field(min_length=1, max_length=500) class QuotaAdjustmentRequest(BaseModel): amount: int reason: str = Field(min_length=1, max_length=500) idempotency_key: str = Field(min_length=8, max_length=120) class VerificationConfirmRequest(BaseModel): challenge_id: UUID code: str = Field(min_length=4, max_length=12) class VerificationSendRequest(BaseModel): channel: Literal["email", "phone"] purpose: Literal["registration", "contact_binding", "contact_change", "password_reset"] def normalize_email(value: str) -> str: return value.strip().lower()