/** * Infrastructure Adapter: InMemoryAchievementRepository * * In-memory implementation of IAchievementRepository */ import { Achievement, AchievementCategory, DRIVER_ACHIEVEMENTS, STEWARD_ACHIEVEMENTS, ADMIN_ACHIEVEMENTS, COMMUNITY_ACHIEVEMENTS, } from '../../domain/entities/Achievement'; import { UserAchievement } from '../../domain/entities/UserAchievement'; import type { IAchievementRepository } from '../../domain/repositories/IAchievementRepository'; export class InMemoryAchievementRepository implements IAchievementRepository { private achievements: Map = new Map(); private userAchievements: Map = new Map(); constructor() { // Seed with predefined achievements this.seedAchievements(); } private seedAchievements(): void { const allAchievements = [ ...DRIVER_ACHIEVEMENTS, ...STEWARD_ACHIEVEMENTS, ...ADMIN_ACHIEVEMENTS, ...COMMUNITY_ACHIEVEMENTS, ]; for (const props of allAchievements) { const achievement = Achievement.create(props); this.achievements.set(achievement.id, achievement); } } // Achievement operations async findAchievementById(id: string): Promise { return this.achievements.get(id) ?? null; } async findAllAchievements(): Promise { return Array.from(this.achievements.values()); } async findAchievementsByCategory(category: AchievementCategory): Promise { return Array.from(this.achievements.values()) .filter(a => a.category === category); } async createAchievement(achievement: Achievement): Promise { if (this.achievements.has(achievement.id)) { throw new Error('Achievement with this ID already exists'); } this.achievements.set(achievement.id, achievement); return achievement; } // UserAchievement operations async findUserAchievementById(id: string): Promise { return this.userAchievements.get(id) ?? null; } async findUserAchievementsByUserId(userId: string): Promise { return Array.from(this.userAchievements.values()) .filter(ua => ua.userId === userId); } async findUserAchievementByUserAndAchievement( userId: string, achievementId: string ): Promise { for (const ua of this.userAchievements.values()) { if (ua.userId === userId && ua.achievementId === achievementId) { return ua; } } return null; } async hasUserEarnedAchievement(userId: string, achievementId: string): Promise { const ua = await this.findUserAchievementByUserAndAchievement(userId, achievementId); return ua !== null && ua.isComplete(); } async createUserAchievement(userAchievement: UserAchievement): Promise { if (this.userAchievements.has(userAchievement.id)) { throw new Error('UserAchievement with this ID already exists'); } this.userAchievements.set(userAchievement.id, userAchievement); return userAchievement; } async updateUserAchievement(userAchievement: UserAchievement): Promise { if (!this.userAchievements.has(userAchievement.id)) { throw new Error('UserAchievement not found'); } this.userAchievements.set(userAchievement.id, userAchievement); return userAchievement; } // Stats async getAchievementLeaderboard(limit: number): Promise<{ userId: string; points: number; count: number }[]> { const userStats = new Map(); for (const ua of this.userAchievements.values()) { if (!ua.isComplete()) continue; const achievement = this.achievements.get(ua.achievementId); if (!achievement) continue; const existing = userStats.get(ua.userId) ?? { points: 0, count: 0 }; userStats.set(ua.userId, { points: existing.points + achievement.points, count: existing.count + 1, }); } return Array.from(userStats.entries()) .map(([userId, stats]) => ({ userId, ...stats })) .sort((a, b) => b.points - a.points) .slice(0, limit); } async getUserAchievementStats(userId: string): Promise<{ total: number; points: number; byCategory: Record }> { const userAchievements = await this.findUserAchievementsByUserId(userId); const completedAchievements = userAchievements.filter(ua => ua.isComplete()); const byCategory: Record = { driver: 0, steward: 0, admin: 0, community: 0, }; let points = 0; for (const ua of completedAchievements) { const achievement = this.achievements.get(ua.achievementId); if (achievement) { points += achievement.points; byCategory[achievement.category]++; } } return { total: completedAchievements.length, points, byCategory, }; } // Test helpers clearUserAchievements(): void { this.userAchievements.clear(); } clear(): void { this.achievements.clear(); this.userAchievements.clear(); } }