""" Pydantic schemas for Shop system """ from datetime import datetime from pydantic import BaseModel, Field from typing import Any # === Shop Items === class ShopItemBase(BaseModel): """Base schema for shop items""" item_type: str code: str name: str description: str | None = None price: int rarity: str = "common" asset_data: dict | None = None class ShopItemCreate(ShopItemBase): """Schema for creating a shop item (admin)""" is_active: bool = True available_from: datetime | None = None available_until: datetime | None = None stock_limit: int | None = None class ShopItemUpdate(BaseModel): """Schema for updating a shop item (admin)""" name: str | None = None description: str | None = None price: int | None = Field(None, ge=1) rarity: str | None = None asset_data: dict | None = None is_active: bool | None = None available_from: datetime | None = None available_until: datetime | None = None stock_limit: int | None = None class ShopItemResponse(ShopItemBase): """Schema for shop item response""" id: int is_active: bool available_from: datetime | None available_until: datetime | None stock_limit: int | None stock_remaining: int | None created_at: datetime is_available: bool # Computed property is_owned: bool = False # Set by API based on user is_equipped: bool = False # Set by API based on user class Config: from_attributes = True # === Inventory === class InventoryItemResponse(BaseModel): """Schema for user inventory item""" id: int item: ShopItemResponse quantity: int equipped: bool purchased_at: datetime expires_at: datetime | None class Config: from_attributes = True # === Purchases === class PurchaseRequest(BaseModel): """Schema for purchase request""" item_id: int quantity: int = Field(default=1, ge=1, le=10) class PurchaseResponse(BaseModel): """Schema for purchase response""" success: bool item: ShopItemResponse quantity: int total_cost: int new_balance: int message: str # === Consumables === class UseConsumableRequest(BaseModel): """Schema for using a consumable""" item_code: str # 'skip', 'boost', 'wild_card', 'lucky_dice', 'copycat', 'undo' marathon_id: int assignment_id: int | None = None # Required for skip, wild_card, copycat game_id: int | None = None # Required for wild_card target_participant_id: int | None = None # Required for copycat class UseConsumableResponse(BaseModel): """Schema for consumable use response""" success: bool item_code: str remaining_quantity: int effect_description: str effect_data: dict | None = None # === Equipment === class EquipItemRequest(BaseModel): """Schema for equipping an item""" inventory_id: int class EquipItemResponse(BaseModel): """Schema for equip response""" success: bool item_type: str equipped_item: ShopItemResponse | None message: str # === Coins === class CoinTransactionResponse(BaseModel): """Schema for coin transaction""" id: int amount: int transaction_type: str description: str | None reference_type: str | None reference_id: int | None created_at: datetime class Config: from_attributes = True class CoinsBalanceResponse(BaseModel): """Schema for coins balance with recent transactions""" balance: int recent_transactions: list[CoinTransactionResponse] class AdminCoinsRequest(BaseModel): """Schema for admin coin operations""" amount: int = Field(..., ge=1) reason: str = Field(..., min_length=1, max_length=500) # === User Cosmetics === class UserCosmeticsResponse(BaseModel): """Schema for user's equipped cosmetics""" frame: ShopItemResponse | None = None title: ShopItemResponse | None = None name_color: ShopItemResponse | None = None background: ShopItemResponse | None = None # === Certification === class CertificationRequestSchema(BaseModel): """Schema for requesting marathon certification""" pass # No fields needed for now class CertificationReviewRequest(BaseModel): """Schema for admin reviewing certification""" approve: bool rejection_reason: str | None = Field(None, max_length=1000) class CertificationStatusResponse(BaseModel): """Schema for certification status""" marathon_id: int certification_status: str is_certified: bool certification_requested_at: datetime | None certified_at: datetime | None certified_by_nickname: str | None = None rejection_reason: str | None = None # === Consumables Status === class ConsumablesStatusResponse(BaseModel): """Schema for participant's consumables status in a marathon""" skips_available: int # From inventory skip_exiles_available: int = 0 # From inventory (skip with exile) skips_used: int # In this marathon skips_remaining: int | None # Based on marathon limit boosts_available: int # From inventory has_active_boost: bool # Currently activated (one-time for current assignment) boost_multiplier: float | None # 1.5 if boost active wild_cards_available: int # From inventory lucky_dice_available: int # From inventory has_lucky_dice: bool # Currently activated lucky_dice_multiplier: float | None # Rolled multiplier if active copycats_available: int # From inventory undos_available: int # From inventory can_undo: bool # Has drop data to undo # === Admin Item Granting === class AdminGrantItemRequest(BaseModel): """Schema for admin granting item to user""" item_id: int quantity: int = Field(default=1, ge=1, le=100) reason: str = Field(..., min_length=1, max_length=500)