""" Pydantic schemas for user data features. Covers sessions, decks, replays, cards, groups, networks, preferences, and activity logs. """ from pydantic import BaseModel, Field from typing import Optional, List, Dict, Any from datetime import datetime from enum import Enum # ===== Enum Types ===== class DeckVersionStatus(str, Enum): DRAFT = "DRAFT" FINAL = "FINAL" ARCHIVED = "ARCHIVED" class GameReplayStatus(str, Enum): IN_PROGRESS = "IN_PROGRESS" COMPLETED = "COMPLETED" FAILED = "FAILED" CANCELLED = "CANCELLED" class GameOutcomeType(str, Enum): WIN = "WIN" LOSS = "LOSS" CONCESSION = "CONCESSION" DISCONNECT = "DISCONNECT" class GroupMemberRole(str, Enum): OWNER = "OWNER" ADMIN = "ADMIN" MEMBER = "MEMBER" class NetworkMemberRole(str, Enum): OWNER = "OWNER" ADMIN = "ADMIN" MEMBER = "MEMBER" class UserPreferenceTheme(str, Enum): LIGHT = "light" DARK = "dark" SYSTEM = "system" class ActivityType(str, Enum): LOGIN = "LOGIN" LOGOUT = "LOGOUT" DECK_EDIT = "DECK_EDIT" GAME_PLAYED = "GAME_PLAYED" CARD_ACQUIRED = "CARD_ACQUIRED" CARD_TRADED = "CARD_TRADED" GROUP_CREATED = "GROUP_CREATED" GROUP_JOINED = "GROUP_JOINED" # ===== Session Schemas ===== class SessionResponse(BaseModel): """User session response.""" id: int user_id: int ip_address: Optional[str] = None user_agent: Optional[str] = None created_at: datetime expires_at: datetime is_active: bool class Config: from_attributes = True class SessionCleanupResponse(BaseModel): """Response after cleaning expired sessions.""" cleaned_count: int message: str # ===== Deck Version Schemas ===== class DeckVersionCreate(BaseModel): """Deck version creation request.""" content: str = Field(..., min_length=1) status: DeckVersionStatus = DeckVersionStatus.DRAFT comment: Optional[str] = None class DeckVersionUpdate(BaseModel): """Deck version update request.""" content: Optional[str] = None status: Optional[DeckVersionStatus] = None comment: Optional[str] = None class DeckVersionResponse(BaseModel): """Deck version response.""" id: int deck_id: int version_number: int content: str status: str comment: Optional[str] created_at: datetime class Config: from_attributes = True class DeckVersionListResponse(BaseModel): """List of deck versions.""" versions: List[DeckVersionResponse] total: int # ===== Game Replay Schemas ===== class GameReplayCreate(BaseModel): """Game replay creation request.""" game_uuid: str = Field(..., min_length=36, max_length=36) room_id: Optional[int] = None game_type: Optional[str] = None format: Optional[str] = None duration_seconds: Optional[int] = None start_time: datetime end_time: Optional[datetime] = None status: GameReplayStatus = GameReplayStatus.IN_PROGRESS replay_data: Optional[Dict[str, Any]] = None class GameReplayUpdate(BaseModel): """Game replay update request.""" room_id: Optional[int] = None game_type: Optional[str] = None format: Optional[str] = None duration_seconds: Optional[int] = None end_time: Optional[datetime] = None status: Optional[GameReplayStatus] = None replay_data: Optional[Dict[str, Any]] = None class GameReplayResponse(BaseModel): """Game replay response.""" id: int game_uuid: str room_id: Optional[int] game_type: Optional[str] format: Optional[str] duration_seconds: Optional[int] start_time: datetime end_time: Optional[datetime] status: str replay_data: Optional[Dict[str, Any]] created_at: datetime updated_at: datetime players: List[Dict[str, Any]] = [] class Config: from_attributes = True class GameReplayListResponse(BaseModel): """List of game replays.""" replays: List[GameReplayResponse] total: int page: int page_size: int total_pages: int # ===== Game Outcome Schemas ===== class GameOutcomeCreate(BaseModel): """Game outcome creation request.""" game_uuid: str outcome: GameOutcomeType opponent_id: Optional[int] = None format: Optional[str] = None rating_before: Optional[int] = None rating_after: Optional[int] = None rating_change: Optional[int] = None class GameOutcomeResponse(BaseModel): """Game outcome response.""" id: int user_id: int game_uuid: str outcome: str opponent_id: Optional[int] format: Optional[str] rating_before: Optional[int] rating_after: Optional[int] rating_change: Optional[int] created_at: datetime class Config: from_attributes = True class GameOutcomeListResponse(BaseModel): """List of game outcomes.""" outcomes: List[GameOutcomeResponse] total: int # ===== User Statistics Schemas ===== class UserStatisticsResponse(BaseModel): """User statistics summary response.""" user_id: int total_games: int total_wins: int total_losses: int total_concessions: int win_rate: float current_streak: int best_streak: int average_rating: float last_game_date: Optional[datetime] updated_at: datetime class Config: from_attributes = True class StatisticsUpdateResponse(BaseModel): """Response after updating statistics.""" user_id: int total_games: int total_wins: int total_losses: int win_rate: float current_streak: int updated_at: datetime # ===== Card Collection Schemas ===== class CardCollectionCreate(BaseModel): """Card collection item creation request.""" card_id: int quantity: int = Field(1, ge=1) condition: str = Field("NEAR_MINT", max_length=20) language: str = Field("EN", max_length=5) is_foil: bool = False is_alt_art: bool = False acquired_date: Optional[datetime] = None acquisition_method: Optional[str] = None notes: Optional[str] = None class CardCollectionUpdate(BaseModel): """Card collection item update request.""" quantity: Optional[int] = None condition: Optional[str] = None language: Optional[str] = None is_foil: Optional[bool] = None is_alt_art: Optional[bool] = None acquired_date: Optional[datetime] = None acquisition_method: Optional[str] = None notes: Optional[str] = None class CardCollectionResponse(BaseModel): """Card collection item response.""" id: int user_id: int card_id: int quantity: int condition: str language: str is_foil: bool is_alt_art: bool acquired_date: Optional[datetime] acquisition_method: Optional[str] notes: Optional[str] created_at: datetime updated_at: datetime class Config: from_attributes = True class CardCollectionListResponse(BaseModel): """List of user card collection.""" cards: List[CardCollectionResponse] total: int page: int page_size: int total_pages: int # ===== Wishlist Schemas ===== class WishlistCreate(BaseModel): """Wishlist item creation request.""" card_id: int max_price: Optional[float] = None notes: Optional[str] = None class WishlistUpdate(BaseModel): """Wishlist item update request.""" max_price: Optional[float] = None notes: Optional[str] = None class WishlistResponse(BaseModel): """Wishlist item response.""" id: int user_id: int card_id: int max_price: Optional[float] notes: Optional[str] created_at: datetime class Config: from_attributes = True class WishlistListResponse(BaseModel): """List of wishlist items.""" items: List[WishlistResponse] total: int # ===== Group Schemas ===== class GroupCreate(BaseModel): """User group creation request.""" name: str = Field(..., min_length=1, max_length=100) description: Optional[str] = None is_public: bool = True max_members: int = Field(50, ge=2, le=500) class GroupUpdate(BaseModel): """User group update request.""" name: Optional[str] = None description: Optional[str] = None is_public: Optional[bool] = None max_members: Optional[int] = None class GroupMemberCreate(BaseModel): """Group member addition request.""" user_id: int role: GroupMemberRole = GroupMemberRole.MEMBER class GroupMemberUpdate(BaseModel): """Group member role update request.""" role: GroupMemberRole class GroupMemberRemove(BaseModel): """Group member removal request.""" user_id: int class GroupResponse(BaseModel): """User group response.""" id: int name: str description: Optional[str] owner_id: int is_public: bool max_members: int created_at: datetime updated_at: datetime member_count: int = 0 is_member: bool = False class Config: from_attributes = True class GroupListResponse(BaseModel): """List of user groups.""" groups: List[GroupResponse] total: int class GroupChatMessageCreate(BaseModel): """Group chat message creation request.""" message: str = Field(..., min_length=1, max_length=2000) class GroupChatMessageResponse(BaseModel): """Group chat message response.""" id: int group_id: int sender_id: int sender_username: Optional[str] = None message: str created_at: datetime class Config: from_attributes = True class GroupChatMessageListResponse(BaseModel): """List of group chat messages.""" messages: List[GroupChatMessageResponse] total: int page: int page_size: int total_pages: int # ===== Network Schemas ===== class NetworkCreate(BaseModel): """User network creation request.""" name: str = Field(..., min_length=1, max_length=100) description: Optional[str] = None is_public: bool = True class NetworkUpdate(BaseModel): """User network update request.""" name: Optional[str] = None description: Optional[str] = None is_public: Optional[bool] = None class NetworkMemberCreate(BaseModel): """Network member addition request.""" user_id: int role: NetworkMemberRole = NetworkMemberRole.MEMBER class NetworkResponse(BaseModel): """User network response.""" id: int name: str description: Optional[str] creator_id: int is_public: bool created_at: datetime member_count: int = 0 is_member: bool = False class Config: from_attributes = True class NetworkListResponse(BaseModel): """List of user networks.""" networks: List[NetworkResponse] total: int # ===== Preference Schemas ===== class UserPreferenceUpdate(BaseModel): """User preference update request.""" theme: Optional[UserPreferenceTheme] = None notifications_enabled: Optional[bool] = None email_notifications: Optional[bool] = None auto_save_decks: Optional[bool] = None default_format: Optional[str] = None language: Optional[str] = None class UserPreferenceResponse(BaseModel): """User preference response.""" user_id: int theme: str notifications_enabled: bool email_notifications: bool auto_save_decks: bool default_format: str language: str updated_at: datetime class Config: from_attributes = True # ===== Activity Log Schemas ===== class ActivityLogEntry(BaseModel): """Activity log entry.""" id: int user_id: int activity_type: str activity_data: Optional[Dict[str, Any]] ip_address: Optional[str] created_at: datetime class Config: from_attributes = True class ActivityLogListResponse(BaseModel): """List of activity log entries.""" entries: List[ActivityLogEntry] total: int page: int page_size: int total_pages: int # ===== Generic Response Schemas ===== class MessageResponse(BaseModel): """Generic message response.""" message: str class CountResponse(BaseModel): """Generic count response.""" count: int class ErrorDetail(BaseModel): """Error detail.""" error: str detail: str