import { PlayerData, GameLibraryError, GameErrorCode } from '../core/types'; import { VALIDATION_RULES } from '../core/config'; import { StorageService } from './StorageService'; /** * PlayerService - manages player data and validation */ export class PlayerService { private currentPlayer: PlayerData | null = null; private storageService: StorageService; constructor(storageService: StorageService) { this.storageService = storageService; } /** * Initialize the service */ async initialize(): Promise { try { // Try to load existing player data const savedPlayer = await this.storageService.getPlayerData(); if (savedPlayer) { this.currentPlayer = savedPlayer; } } catch (error) { console.warn('Failed to load player data:', error); } } /** * Set player data with validation */ setPlayerData(data: PlayerData): void { this.validatePlayerData(data); const processedData: PlayerData = { ...data, createdAt: data.createdAt || new Date(), lastActive: new Date(), totalGamesPlayed: data.totalGamesPlayed || 0, achievements: data.achievements || [] }; this.currentPlayer = processedData; // Save to storage this.storageService.savePlayerData(processedData).catch(error => { console.warn('Failed to save player data:', error); }); } /** * Get current player data */ getPlayerData(): PlayerData | null { return this.currentPlayer ? { ...this.currentPlayer } : null; } /** * Update player's last active time */ updateLastActive(): void { if (this.currentPlayer) { this.currentPlayer.lastActive = new Date(); this.storageService.savePlayerData(this.currentPlayer).catch(error => { console.warn('Failed to update last active:', error); }); } } /** * Increment games played counter */ incrementGamesPlayed(): void { if (this.currentPlayer) { this.currentPlayer.totalGamesPlayed += 1; this.updateLastActive(); } } /** * Add achievement to player */ addAchievement(achievement: any): void { if (this.currentPlayer) { // Check if achievement already exists const exists = this.currentPlayer.achievements.some(a => a.id === achievement.id); if (!exists) { this.currentPlayer.achievements.push({ ...achievement, unlockedAt: new Date() }); this.storageService.savePlayerData(this.currentPlayer).catch(error => { console.warn('Failed to save achievement:', error); }); } } } /** * Validate player data */ private validatePlayerData(data: PlayerData): void { // Validate player ID if (!data.playerId) { throw new GameLibraryError( 'Player ID is required', GameErrorCode.INVALID_PLAYER_DATA ); } if (data.playerId.length < VALIDATION_RULES.PLAYER_ID.MIN_LENGTH || data.playerId.length > VALIDATION_RULES.PLAYER_ID.MAX_LENGTH) { throw new GameLibraryError( `Player ID must be between ${VALIDATION_RULES.PLAYER_ID.MIN_LENGTH} and ${VALIDATION_RULES.PLAYER_ID.MAX_LENGTH} characters`, GameErrorCode.INVALID_PLAYER_DATA ); } if (!VALIDATION_RULES.PLAYER_ID.PATTERN.test(data.playerId)) { throw new GameLibraryError( 'Player ID contains invalid characters', GameErrorCode.INVALID_PLAYER_DATA ); } // Validate optional fields if (data.email && !VALIDATION_RULES.EMAIL.PATTERN.test(data.email)) { throw new GameLibraryError( 'Invalid email format', GameErrorCode.INVALID_PLAYER_DATA ); } if (data.phoneNumber && !VALIDATION_RULES.PHONE_NUMBER.PATTERN.test(data.phoneNumber)) { throw new GameLibraryError( 'Invalid phone number format', GameErrorCode.INVALID_PLAYER_DATA ); } if (data.idNumber && !VALIDATION_RULES.ID_NUMBER.PATTERN.test(data.idNumber)) { throw new GameLibraryError( 'Invalid ID number format', GameErrorCode.INVALID_PLAYER_DATA ); } if (data.age !== undefined && (data.age < 0 || data.age > 150)) { throw new GameLibraryError( 'Age must be between 0 and 150', GameErrorCode.INVALID_PLAYER_DATA ); } // Validate name lengths if (data.firstName && data.firstName.length > 50) { throw new GameLibraryError( 'First name is too long (max 50 characters)', GameErrorCode.INVALID_PLAYER_DATA ); } if (data.lastName && data.lastName.length > 50) { throw new GameLibraryError( 'Last name is too long (max 50 characters)', GameErrorCode.INVALID_PLAYER_DATA ); } } /** * Clear current player data */ clearPlayerData(): void { this.currentPlayer = null; // Note: StorageService doesn't have clearPlayerData method // Data will be overwritten when new player data is set } /** * Get player statistics */ getPlayerStats(): { totalGames: number; totalAchievements: number; memberSince: Date | null; lastPlayed: Date | null; } { if (!this.currentPlayer) { return { totalGames: 0, totalAchievements: 0, memberSince: null, lastPlayed: null }; } return { totalGames: this.currentPlayer.totalGamesPlayed, totalAchievements: this.currentPlayer.achievements.length, memberSince: this.currentPlayer.createdAt, lastPlayed: this.currentPlayer.lastActive }; } }