from pydantic import BaseModel, field_validator from datetime import datetime, date from decimal import Decimal from typing import Optional import uuid class PropertyBrief(BaseModel): id: str title: str slug: str model_config = {"from_attributes": True} class PropertyCard(BaseModel): id: str title: str slug: str price: Optional[Decimal] = None city: Optional[str] = None neighborhood: Optional[str] = None cover_photo_url: Optional[str] = None model_config = {"from_attributes": True} @classmethod def from_property(cls, prop) -> "PropertyCard": cover = prop.photos[0].url if prop.photos else None city = prop.city.name if prop.city else None neighborhood = prop.neighborhood.name if prop.neighborhood else None return cls( id=str(prop.id), title=prop.title, slug=prop.slug, price=prop.price, city=city, neighborhood=neighborhood, cover_photo_url=cover, ) class SavedPropertyOut(BaseModel): id: str property_id: Optional[str] property: Optional[PropertyCard] created_at: datetime model_config = {"from_attributes": True} @classmethod def from_saved(cls, saved) -> "SavedPropertyOut": prop_card = PropertyCard.from_property(saved.property) if saved.property else None return cls( id=str(saved.id), property_id=str(saved.property_id) if saved.property_id else None, property=prop_card, created_at=saved.created_at, ) class FavoriteIn(BaseModel): property_id: str @field_validator("property_id") @classmethod def validate_uuid(cls, v: str) -> str: try: uuid.UUID(v) except ValueError: raise ValueError("property_id deve ser um UUID válido") return v class VisitRequestOut(BaseModel): id: str property: Optional[PropertyBrief] message: Optional[str] status: str scheduled_at: Optional[datetime] created_at: datetime model_config = {"from_attributes": True} class VisitStatusIn(BaseModel): status: str scheduled_at: Optional[datetime] = None @field_validator("status") @classmethod def validate_status(cls, v: str) -> str: allowed = {"pending", "confirmed", "cancelled", "completed"} if v not in allowed: raise ValueError(f'Status deve ser um de: {", ".join(allowed)}') return v class BoletoOut(BaseModel): id: str property: Optional[PropertyBrief] description: str amount: Decimal due_date: date status: str url: Optional[str] created_at: datetime model_config = {"from_attributes": True} class BoletoCreateIn(BaseModel): user_id: str property_id: Optional[str] = None description: str amount: Decimal due_date: date url: Optional[str] = None @field_validator("description") @classmethod def description_not_empty(cls, v: str) -> str: if not v.strip(): raise ValueError("Descrição não pode ser vazia") return v.strip() @field_validator("amount") @classmethod def amount_positive(cls, v: Decimal) -> Decimal: if v <= 0: raise ValueError("Valor deve ser positivo") return v class UpdateProfileIn(BaseModel): name: str @field_validator("name") @classmethod def name_not_empty(cls, v: str) -> str: if not v.strip(): raise ValueError("Nome não pode ser vazio") return v.strip() class UpdateProfileOut(BaseModel): id: str name: str email: str model_config = {"from_attributes": True} class UpdatePasswordIn(BaseModel): current_password: str new_password: str @field_validator("new_password") @classmethod def min_length(cls, v: str) -> str: if len(v) < 8: raise ValueError("A nova senha deve ter pelo menos 8 caracteres") return v