package models import ( "database/sql" "time" ) // Player represents an anonymous player identified by a client-generated UUID. type Player struct { ID int64 `json:"-"` ClientID string `json:"client_id"` FirstSeen time.Time `json:"first_seen"` LastSeen time.Time `json:"last_seen"` TotalSessions int `json:"total_sessions"` } // PlayerIP tracks distinct IP addresses a player has connected from. type PlayerIP struct { ID int64 `json:"-"` PlayerID int64 `json:"-"` IPAddress string `json:"ip_address"` FirstSeen time.Time `json:"first_seen"` LastSeen time.Time `json:"last_seen"` } // GameSession represents a single play session. type GameSession struct { ID string `json:"session_id"` PlayerID int64 `json:"-"` Score int `json:"score"` LevelReached int `json:"level_reached"` LivesUsed int `json:"lives_used"` DurationSeconds int `json:"duration_seconds"` EndReason string `json:"end_reason"` StartedAt time.Time `json:"started_at"` EndedAt time.Time `json:"ended_at"` } // DeviceInfo holds browser/device fingerprint data captured per session. type DeviceInfo struct { ID int64 `json:"-"` SessionID string `json:"-"` IPAddress string `json:"ip_address"` UserAgent string `json:"user_agent"` Platform string `json:"platform"` Language string `json:"language"` ScreenWidth sql.NullInt32 `json:"screen_width"` ScreenHeight sql.NullInt32 `json:"screen_height"` DevicePixelRatio sql.NullFloat64 `json:"device_pixel_ratio"` Timezone string `json:"timezone"` WebGLRenderer string `json:"webgl_renderer"` TouchSupport bool `json:"touch_support"` } // HighScore is a denormalized personal-best record, one per player. type HighScore struct { ID int64 `json:"-"` PlayerID int64 `json:"-"` Score int `json:"score"` SessionID sql.NullString `json:"-"` AchievedAt time.Time `json:"achieved_at"` } // --- Request/Response types --- // DeviceInfoRequest is the optional device payload in session start. type DeviceInfoRequest struct { Platform string `json:"platform"` Language string `json:"language"` ScreenWidth *int `json:"screen_width"` ScreenHeight *int `json:"screen_height"` DevicePixelRatio *float64 `json:"device_pixel_ratio"` Timezone string `json:"timezone"` WebGLRenderer string `json:"webgl_renderer"` TouchSupport bool `json:"touch_support"` } // SessionStartRequest is the JSON body for POST /session/start/. type SessionStartRequest struct { ClientID string `json:"client_id"` Device *DeviceInfoRequest `json:"device"` } // SessionEndRequest is the JSON body for POST /session/:id/end/. type SessionEndRequest struct { Score int `json:"score"` LevelReached int `json:"level_reached"` LivesUsed int `json:"lives_used"` DurationSeconds int `json:"duration_seconds"` EndReason string `json:"end_reason"` } // LeaderboardEntry is a single row in the leaderboard response. type LeaderboardEntry struct { ClientID string `json:"client_id"` Score int `json:"score"` AchievedAt time.Time `json:"achieved_at"` } // SessionSummary is a session entry in the player stats response. type SessionSummary struct { SessionID string `json:"session_id"` Score int `json:"score"` LevelReached int `json:"level_reached"` LivesUsed int `json:"lives_used"` DurationSeconds int `json:"duration_seconds"` EndReason string `json:"end_reason"` StartedAt time.Time `json:"started_at"` EndedAt time.Time `json:"ended_at"` } // PlayerStatsResponse is the response for GET /player/:client_id/. type PlayerStatsResponse struct { ClientID string `json:"client_id"` TotalSessions int `json:"total_sessions"` FirstSeen time.Time `json:"first_seen"` HighScore *int `json:"high_score"` Sessions []SessionSummary `json:"sessions"` }