import { MediaReference } from '@core/domain/media/MediaReference'; import { Driver } from '@core/racing/domain/entities/Driver'; import { DriverRepository } from '@core/racing/domain/repositories/DriverRepository'; import { Logger } from '@core/shared/domain'; export class InMemoryDriverRepository implements DriverRepository { private drivers: Map = new Map(); private iracingIdIndex: Map = new Map(); // iracingId -> driverId constructor(private readonly logger: Logger) { this.logger.info('InMemoryDriverRepository initialized.'); } async findById(id: string): Promise { this.logger.debug(`[InMemoryDriverRepository] Finding driver by ID: ${id}`); const driver = this.drivers.get(id) ?? null; if (driver) { this.logger.info(`Found driver by ID: ${id}.`); } else { this.logger.warn(`Driver with ID ${id} not found.`); } return Promise.resolve(driver); } async findByIRacingId(iracingId: string): Promise { this.logger.debug(`[InMemoryDriverRepository] Finding driver by iRacing ID: ${iracingId}`); const driverId = this.iracingIdIndex.get(iracingId); if (!driverId) { this.logger.warn(`Driver with iRacing ID ${iracingId} not found.`); return Promise.resolve(null); } return this.findById(driverId); } async findAll(): Promise { this.logger.debug('[InMemoryDriverRepository] Finding all drivers.'); return Promise.resolve(Array.from(this.drivers.values())); } async create(driver: Driver): Promise { this.logger.debug(`[InMemoryDriverRepository] Creating driver: ${driver.id} (iRacing ID: ${driver.iracingId.toString()})`); if (this.drivers.has(driver.id)) { this.logger.warn(`Driver with ID ${driver.id} already exists.`); throw new Error('Driver already exists'); } if (this.iracingIdIndex.has(driver.iracingId.toString())) { this.logger.warn(`Driver with iRacing ID ${driver.iracingId.toString()} already exists.`); throw new Error('iRacing ID already registered'); } this.drivers.set(driver.id, driver); this.iracingIdIndex.set(driver.iracingId.toString(), driver.id); this.logger.info(`Driver ${driver.id} created successfully.`); return Promise.resolve(driver); } async update(driver: Driver): Promise { this.logger.debug(`[InMemoryDriverRepository] Updating driver: ${driver.id} (iRacing ID: ${driver.iracingId.toString()})`); if (!this.drivers.has(driver.id)) { this.logger.warn(`Driver with ID ${driver.id} not found for update.`); throw new Error('Driver not found'); } const existingDriver = this.drivers.get(driver.id); this.drivers.set(driver.id, driver); // Re-index iRacing ID if it changed if (existingDriver && existingDriver.iracingId.toString() !== driver.iracingId.toString()) { this.iracingIdIndex.delete(existingDriver.iracingId.toString()); this.iracingIdIndex.set(driver.iracingId.toString(), driver.id); } this.logger.info(`Driver ${driver.id} updated successfully.`); return Promise.resolve(driver); } async delete(id: string): Promise { this.logger.debug(`[InMemoryDriverRepository] Deleting driver with ID: ${id}`); const driver = this.drivers.get(id); if (driver) { this.drivers.delete(id); this.iracingIdIndex.delete(driver.iracingId.toString()); this.logger.info(`Driver ${id} deleted successfully.`); } else { this.logger.warn(`Driver with ID ${id} not found for deletion.`); } return Promise.resolve(); } async exists(id: string): Promise { this.logger.debug(`[InMemoryDriverRepository] Checking existence of driver with ID: ${id}`); return Promise.resolve(this.drivers.has(id)); } async existsByIRacingId(iracingId: string): Promise { this.logger.debug(`[InMemoryDriverRepository] Checking existence of driver with iRacing ID: ${iracingId}`); return Promise.resolve(this.iracingIdIndex.has(iracingId)); } // Serialization methods for persistence serialize(driver: Driver): Record { return { id: driver.id, iracingId: driver.iracingId.toString(), name: driver.name.toString(), country: driver.country.toString(), bio: driver.bio?.toString() ?? null, joinedAt: driver.joinedAt.toDate().toISOString(), category: driver.category ?? null, avatarRef: driver.avatarRef.toJSON(), }; } deserialize(data: Record): Driver { const props: { id: string; iracingId: string; name: string; country: string; bio?: string; joinedAt: Date; category?: string; avatarRef?: MediaReference; } = { id: data.id as string, iracingId: data.iracingId as string, name: data.name as string, country: data.country as string, joinedAt: new Date(data.joinedAt as string), }; if (data.bio !== null && data.bio !== undefined) { props.bio = data.bio as string; } if (data.category !== null && data.category !== undefined) { props.category = data.category as string; } if (data.avatarRef !== null && data.avatarRef !== undefined) { props.avatarRef = MediaReference.fromJSON(data.avatarRef as Record); } return Driver.rehydrate(props); } }