from datetime import date, datetime from pydantic import BaseModel, EmailStr, Field class Message(BaseModel): message: str class SetupStatus(BaseModel): needs_setup: bool instance: dict | None = None class SetupCreate(BaseModel): name: str = Field(min_length=1, max_length=160) email: EmailStr password: str = Field(min_length=12, max_length=256) language: str theme: str public_url: str = Field(min_length=1, max_length=500) instance_name: str = Field(min_length=1, max_length=160) timezone: str = Field(min_length=1, max_length=80) class LoginRequest(BaseModel): email: EmailStr password: str = Field(min_length=1, max_length=256) class UserOut(BaseModel): id: str email: EmailStr name: str instance_role: str language: str theme: str timezone: str is_active: bool onboarding_completed: bool model_config = {"from_attributes": True} class UserCreate(BaseModel): email: EmailStr name: str = Field(min_length=1, max_length=160) role: str = "user" send_invite: bool = True class UserUpdate(BaseModel): name: str | None = Field(default=None, min_length=1, max_length=160) language: str | None = None theme: str | None = None timezone: str | None = None instance_role: str | None = None is_active: bool | None = None onboarding_completed: bool | None = None class InviteAccept(BaseModel): token: str name: str = Field(min_length=1, max_length=160) password: str = Field(min_length=12, max_length=256) language: str theme: str home_name: str | None = Field(default=None, max_length=160) join_code: str | None = Field(default=None, max_length=40) class HomeOut(BaseModel): id: str name: str expiry_warning_days: int daily_summary_enabled: bool daily_summary_time: str role: str | None = None model_config = {"from_attributes": True} class HomeCreate(BaseModel): name: str = Field(min_length=1, max_length=160) class HomeSettingsUpdate(BaseModel): name: str | None = Field(default=None, min_length=1, max_length=160) expiry_warning_days: int | None = Field(default=None, ge=0, le=60) daily_summary_enabled: bool | None = None daily_summary_time: str | None = Field(default=None, pattern=r"^\d{2}:\d{2}$") class JoinCodeOut(BaseModel): join_code: str expires_at: datetime class ProductIn(BaseModel): name: str = Field(min_length=1, max_length=220) barcode: str | None = Field(default=None, max_length=80) brand: str | None = Field(default=None, max_length=160) category: str = Field(default="Other", max_length=120) location: str = Field(default="Pantry", max_length=120) quantity: float = Field(default=1, ge=0) unit: str = Field(default="pcs", max_length=32) expires_at: date | None = None min_quantity: float = Field(default=0, ge=0) notes: str | None = Field(default=None, max_length=5000) image_url: str | None = Field(default=None, max_length=1000) class ProductOut(ProductIn): id: str home_id: str status: str model_config = {"from_attributes": True} class ShoppingItemIn(BaseModel): name: str = Field(min_length=1, max_length=220) category: str = Field(default="Other", max_length=120) quantity: float = Field(default=1, ge=0) unit: str = Field(default="pcs", max_length=32) product_id: str | None = None class ShoppingItemOut(ShoppingItemIn): id: str home_id: str checked: bool model_config = {"from_attributes": True} class MailSettingsIn(BaseModel): smtp_host: str | None = Field(default=None, max_length=220) smtp_port: int = Field(default=587, ge=1, le=65535) smtp_user: str | None = Field(default=None, max_length=220) smtp_password: str | None = Field(default=None, max_length=1000) use_tls: bool = True use_starttls: bool = True sender_address: EmailStr | None = None sender_name: str = Field(default="NexaPantry", max_length=160) class MailSettingsOut(BaseModel): smtp_host: str | None smtp_port: int smtp_user: str | None has_password: bool use_tls: bool use_starttls: bool sender_address: EmailStr | None sender_name: str class TestMailIn(BaseModel): to: EmailStr