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<string, Driver> = new Map();
  private iracingIdIndex: Map<string, string> = new Map(); // iracingId -> driverId

  constructor(private readonly logger: Logger) {
    this.logger.info('InMemoryDriverRepository initialized.');
  }

  async findById(id: string): Promise<Driver | null> {
    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<Driver | null> {
    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<Driver[]> {
    this.logger.debug('[InMemoryDriverRepository] Finding all drivers.');
    return Promise.resolve(Array.from(this.drivers.values()));
  }

  async create(driver: Driver): Promise<Driver> {
    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<Driver> {
    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<void> {
    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<boolean> {
    this.logger.debug(`[InMemoryDriverRepository] Checking existence of driver with ID: ${id}`);
    return Promise.resolve(this.drivers.has(id));
  }

  async existsByIRacingId(iracingId: string): Promise<boolean> {
    this.logger.debug(`[InMemoryDriverRepository] Checking existence of driver with iRacing ID: ${iracingId}`);
    return Promise.resolve(this.iracingIdIndex.has(iracingId));
  }

  async clear(): Promise<void> {
    this.logger.info('[InMemoryDriverRepository] Clearing all drivers');
    this.drivers.clear();
    this.iracingIdIndex.clear();
  }

  // Serialization methods for persistence
  serialize(driver: Driver): Record<string, unknown> {
    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<string, unknown>): 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<string, unknown>);
    }

    return Driver.rehydrate(props);
  }
}