import { Repository, Not, In } from 'typeorm'; import { AppDataSource } from '../ormconfig'; import { GameAggregate, GameState } from '../../Domain/Game/GameAggregate'; import { IGameRepository } from '../../Domain/IRepository/IGameRepository'; import { logDatabase, logError } from '../../Application/Services/Logger'; export class GameRepository implements IGameRepository { private repo: Repository; constructor() { this.repo = AppDataSource.getRepository(GameAggregate); } async create(game: Partial): Promise { const startTime = performance.now(); try { const result = await this.repo.save(game); const endTime = performance.now(); logDatabase('Game created', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${result.id}, gameCode: ${result.gamecode}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game creation failed', `executionTime: ${Math.round(endTime - startTime)}ms`); logError('GameRepository.create error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to create game in database'); } } async findByPage(from: number, to: number): Promise<{ games: GameAggregate[], totalCount: number }> { const startTime = performance.now(); try { const limit = to - from + 1; const offset = from; // Get total count for pagination const totalCount = await this.repo.count({ where: { state: Not(GameState.CANCELLED) } }); // Get paginated results const games = await this.repo.find({ where: { state: Not(GameState.CANCELLED) }, order: { updateDate: 'DESC' }, take: limit, skip: offset }); const endTime = performance.now(); logDatabase('Game page query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}, total: ${totalCount}, from: ${from}, to: ${to}`); return { games, totalCount }; } catch (error) { const endTime = performance.now(); logDatabase('Game page query failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); logError('GameRepository.findByPage error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to get games page from database'); } } async findByPageIncludingDeleted(from: number, to: number): Promise<{ games: GameAggregate[], totalCount: number }> { const startTime = performance.now(); try { const limit = to - from + 1; const offset = from; // Get total count for pagination (including deleted) const totalCount = await this.repo.count(); // Get paginated results (including deleted) const games = await this.repo.find({ order: { updateDate: 'DESC' }, take: limit, skip: offset }); const endTime = performance.now(); logDatabase('Game page query (including deleted) completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}, total: ${totalCount}, from: ${from}, to: ${to}`); return { games, totalCount }; } catch (error) { const endTime = performance.now(); logDatabase('Game page query (including deleted) failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); logError('GameRepository.findByPageIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to get games page (including deleted) from database'); } } async findById(id: string): Promise { const startTime = performance.now(); try { const result = await this.repo.findOne({ where: { id, state: Not(GameState.CANCELLED) } }); const endTime = performance.now(); logDatabase('Game findById completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, found: ${!!result}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game findById failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`); logError('GameRepository.findById error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find game by id in database'); } } async findByIdIncludingDeleted(id: string): Promise { const startTime = performance.now(); try { const result = await this.repo.findOne({ where: { id } }); const endTime = performance.now(); logDatabase('Game findByIdIncludingDeleted completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, found: ${!!result}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game findByIdIncludingDeleted failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`); logError('GameRepository.findByIdIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find game by id (including deleted) in database'); } } async findByGameCode(gamecode: string): Promise { const startTime = performance.now(); try { const result = await this.repo.findOne({ where: { gamecode, state: Not(GameState.CANCELLED) } }); const endTime = performance.now(); logDatabase('Game findByGameCode completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameCode: ${gamecode}, found: ${!!result}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game findByGameCode failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameCode: ${gamecode}`); logError('GameRepository.findByGameCode error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find game by game code in database'); } } async search(query: string, limit?: number, offset?: number): Promise<{ games: GameAggregate[], totalCount: number }> { const startTime = performance.now(); try { const queryBuilder = this.repo.createQueryBuilder('game') .where('game.state != :cancelledState', { cancelledState: GameState.CANCELLED }) .andWhere('(game.gamecode ILIKE :query)', { query: `%${query}%` }); // Get total count const totalCount = await queryBuilder.getCount(); // Apply pagination if provided if (limit !== undefined) { queryBuilder.take(limit); } if (offset !== undefined) { queryBuilder.skip(offset); } const games = await queryBuilder.orderBy('game.updateDate', 'DESC').getMany(); const endTime = performance.now(); logDatabase('Game search completed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}, found: ${games.length}, total: ${totalCount}`); return { games, totalCount }; } catch (error) { const endTime = performance.now(); logDatabase('Game search failed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}`); logError('GameRepository.search error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to search games in database'); } } async searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ games: GameAggregate[], totalCount: number }> { const startTime = performance.now(); try { const queryBuilder = this.repo.createQueryBuilder('game') .where('(game.gamecode ILIKE :query)', { query: `%${query}%` }); // Get total count const totalCount = await queryBuilder.getCount(); // Apply pagination if provided if (limit !== undefined) { queryBuilder.take(limit); } if (offset !== undefined) { queryBuilder.skip(offset); } const games = await queryBuilder.orderBy('game.updateDate', 'DESC').getMany(); const endTime = performance.now(); logDatabase('Game search (including deleted) completed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}, found: ${games.length}, total: ${totalCount}`); return { games, totalCount }; } catch (error) { const endTime = performance.now(); logDatabase('Game search (including deleted) failed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}`); logError('GameRepository.searchIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to search games (including deleted) in database'); } } async update(id: string, update: Partial): Promise { const startTime = performance.now(); try { await this.repo.update(id, update); const result = await this.findById(id); const endTime = performance.now(); logDatabase('Game update completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, updated: ${!!result}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game update failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`); logError('GameRepository.update error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to update game in database'); } } async delete(id: string): Promise { const startTime = performance.now(); try { const result = await this.repo.delete(id); const endTime = performance.now(); logDatabase('Game delete completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, affected: ${result.affected}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game delete failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`); logError('GameRepository.delete error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to delete game from database'); } } async softDelete(id: string): Promise { const startTime = performance.now(); try { await this.repo.update(id, { state: GameState.CANCELLED }); const result = await this.findByIdIncludingDeleted(id); const endTime = performance.now(); logDatabase('Game soft delete completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, updated: ${!!result}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game soft delete failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`); logError('GameRepository.softDelete error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to soft delete game in database'); } } // Game-specific methods async findActiveGames(): Promise { const startTime = performance.now(); try { const games = await this.repo.find({ where: { state: GameState.ACTIVE }, order: { updateDate: 'DESC' } }); const endTime = performance.now(); logDatabase('Active games query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}`); return games; } catch (error) { const endTime = performance.now(); logDatabase('Active games query failed', `executionTime: ${Math.round(endTime - startTime)}ms`); logError('GameRepository.findActiveGames error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find active games in database'); } } async findGamesByPlayer(playerId: string): Promise { const startTime = performance.now(); try { const queryBuilder = this.repo.createQueryBuilder('game') .where('game.state != :cancelledState', { cancelledState: GameState.CANCELLED }) .andWhere('JSON_CONTAINS(game.players, :playerId)', { playerId: `"${playerId}"` }) .orderBy('game.updateDate', 'DESC'); const games = await queryBuilder.getMany(); const endTime = performance.now(); logDatabase('Games by player query completed', `executionTime: ${Math.round(endTime - startTime)}ms, playerId: ${playerId}, found: ${games.length}`); return games; } catch (error) { const endTime = performance.now(); logDatabase('Games by player query failed', `executionTime: ${Math.round(endTime - startTime)}ms, playerId: ${playerId}`); logError('GameRepository.findGamesByPlayer error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find games by player in database'); } } async findWaitingGames(): Promise { const startTime = performance.now(); try { const games = await this.repo.find({ where: { state: GameState.WAITING }, order: { createdate: 'ASC' } }); const endTime = performance.now(); logDatabase('Waiting games query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}`); return games; } catch (error) { const endTime = performance.now(); logDatabase('Waiting games query failed', `executionTime: ${Math.round(endTime - startTime)}ms`); logError('GameRepository.findWaitingGames error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find waiting games in database'); } } async findFinishedGames(from?: number, to?: number): Promise<{ games: GameAggregate[], totalCount: number }> { const startTime = performance.now(); try { const queryBuilder = this.repo.createQueryBuilder('game') .where('game.state = :finishedState', { finishedState: GameState.FINISHED }) .orderBy('game.enddate', 'DESC'); // Get total count const totalCount = await queryBuilder.getCount(); // Apply pagination if provided if (from !== undefined && to !== undefined) { const limit = to - from + 1; const offset = from; queryBuilder.take(limit).skip(offset); } const games = await queryBuilder.getMany(); const endTime = performance.now(); logDatabase('Finished games query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}, total: ${totalCount}`); return { games, totalCount }; } catch (error) { const endTime = performance.now(); logDatabase('Finished games query failed', `executionTime: ${Math.round(endTime - startTime)}ms`); logError('GameRepository.findFinishedGames error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to find finished games in database'); } } async addPlayerToGame(gameId: string, playerId: string): Promise { const startTime = performance.now(); try { const game = await this.findById(gameId); if (!game) { return null; } // Check if player is already in the game if (game.players.includes(playerId)) { return game; } // Check if game is full if (game.players.length >= game.maxplayers) { throw new Error('Game is full'); } const updatedPlayers = [...game.players, playerId]; const result = await this.update(gameId, { players: updatedPlayers }); const endTime = performance.now(); logDatabase('Player added to game', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Add player to game failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`); logError('GameRepository.addPlayerToGame error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to add player to game in database'); } } async removePlayerFromGame(gameId: string, playerId: string): Promise { const startTime = performance.now(); try { const game = await this.findById(gameId); if (!game) { return null; } const updatedPlayers = game.players.filter(id => id !== playerId); const result = await this.update(gameId, { players: updatedPlayers }); const endTime = performance.now(); logDatabase('Player removed from game', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Remove player from game failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`); logError('GameRepository.removePlayerFromGame error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to remove player from game in database'); } } async updateGameState(gameId: string, started: boolean, finished?: boolean, winner?: string): Promise { const startTime = performance.now(); try { const updateData: Partial = { started }; if (started && !finished) { updateData.state = GameState.ACTIVE; updateData.startdate = new Date(); } if (finished) { updateData.finished = true; updateData.state = GameState.FINISHED; updateData.enddate = new Date(); if (winner) { updateData.winner = winner; } } const result = await this.update(gameId, updateData); const endTime = performance.now(); logDatabase('Game state updated', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, started: ${started}, finished: ${finished}, winner: ${winner}`); return result; } catch (error) { const endTime = performance.now(); logDatabase('Game state update failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}`); logError('GameRepository.updateGameState error', error instanceof Error ? error : new Error(String(error))); throw new Error('Failed to update game state in database'); } } }