package valueobjects import ( "fmt" "time" "github.com/knowfoolery/backend/shared/errors" "github.com/knowfoolery/backend/shared/types" ) type LeaderboardEntry struct { playerID types.UserID playerName string score int32 rank int32 achievedAt time.Time sessionID types.GameSessionID gamesPlayed int32 avgScore float32 lastActivity time.Time metadata map[string]interface{} } func NewLeaderboardEntry( playerID types.UserID, playerName string, score int32, sessionID types.GameSessionID, ) (*LeaderboardEntry, error) { if playerID == "" { return nil, errors.ErrInvalidPlayerID } if playerName == "" { return nil, errors.ErrInvalidPlayerName } if score < 0 { return nil, errors.ErrInvalidScore } if sessionID == "" { return nil, errors.ErrInvalidSessionID } now := time.Now() return &LeaderboardEntry{ playerID: playerID, playerName: playerName, score: score, rank: 0, achievedAt: now, sessionID: sessionID, gamesPlayed: 1, avgScore: float32(score), lastActivity: now, metadata: make(map[string]interface{}), }, nil } func (le *LeaderboardEntry) PlayerID() types.UserID { return le.playerID } func (le *LeaderboardEntry) PlayerName() string { return le.playerName } func (le *LeaderboardEntry) Score() int32 { return le.score } func (le *LeaderboardEntry) Rank() int32 { return le.rank } func (le *LeaderboardEntry) AchievedAt() time.Time { return le.achievedAt } func (le *LeaderboardEntry) SessionID() types.GameSessionID { return le.sessionID } func (le *LeaderboardEntry) GamesPlayed() int32 { return le.gamesPlayed } func (le *LeaderboardEntry) AvgScore() float32 { return le.avgScore } func (le *LeaderboardEntry) LastActivity() time.Time { return le.lastActivity } func (le *LeaderboardEntry) Metadata() map[string]interface{} { result := make(map[string]interface{}) for k, v := range le.metadata { result[k] = v } return result } func (le *LeaderboardEntry) UpdateScore(newScore int32, sessionID types.GameSessionID) error { if newScore < 0 { return errors.ErrInvalidScore } if sessionID == "" { return errors.ErrInvalidSessionID } if newScore > le.score { le.score = newScore le.sessionID = sessionID le.achievedAt = time.Now() } le.gamesPlayed++ le.avgScore = (le.avgScore*float32(le.gamesPlayed-1) + float32(newScore)) / float32(le.gamesPlayed) le.lastActivity = time.Now() return nil } func (le *LeaderboardEntry) SetRank(rank int32) error { if rank <= 0 { return errors.ErrInvalidRank } le.rank = rank return nil } func (le *LeaderboardEntry) UpdatePlayerName(name string) error { if name == "" { return errors.ErrInvalidPlayerName } le.playerName = name return nil } func (le *LeaderboardEntry) SetMetadata(key string, value interface{}) error { if key == "" { return errors.ErrInvalidMetadata } le.metadata[key] = value return nil } func (le *LeaderboardEntry) RemoveMetadata(key string) { delete(le.metadata, key) } func (le *LeaderboardEntry) IsEqual(other *LeaderboardEntry) bool { if other == nil { return false } return le.playerID == other.playerID && le.score == other.score && le.sessionID == other.sessionID } func (le *LeaderboardEntry) String() string { return fmt.Sprintf("LeaderboardEntry{PlayerID: %s, Name: %s, Score: %d, Rank: %d}", le.playerID, le.playerName, le.score, le.rank) } type LeaderboardEntrySnapshot struct { PlayerID types.UserID `json:"player_id"` PlayerName string `json:"player_name"` Score int32 `json:"score"` Rank int32 `json:"rank"` AchievedAt time.Time `json:"achieved_at"` SessionID types.GameSessionID `json:"session_id"` GamesPlayed int32 `json:"games_played"` AvgScore float32 `json:"avg_score"` LastActivity time.Time `json:"last_activity"` Metadata map[string]interface{} `json:"metadata"` } func (le *LeaderboardEntry) ToSnapshot() LeaderboardEntrySnapshot { return LeaderboardEntrySnapshot{ PlayerID: le.playerID, PlayerName: le.playerName, Score: le.score, Rank: le.rank, AchievedAt: le.achievedAt, SessionID: le.sessionID, GamesPlayed: le.gamesPlayed, AvgScore: le.avgScore, LastActivity: le.lastActivity, Metadata: le.Metadata(), } } func (le *LeaderboardEntry) FromSnapshot(snapshot LeaderboardEntrySnapshot) error { if snapshot.PlayerID == "" { return errors.ErrInvalidPlayerID } if snapshot.PlayerName == "" { return errors.ErrInvalidPlayerName } if snapshot.Score < 0 { return errors.ErrInvalidScore } le.playerID = snapshot.PlayerID le.playerName = snapshot.PlayerName le.score = snapshot.Score le.rank = snapshot.Rank le.achievedAt = snapshot.AchievedAt le.sessionID = snapshot.SessionID le.gamesPlayed = snapshot.GamesPlayed le.avgScore = snapshot.AvgScore le.lastActivity = snapshot.LastActivity le.metadata = make(map[string]interface{}) for k, v := range snapshot.Metadata { le.metadata[k] = v } return nil }