/**
 * Infrastructure Adapter: InMemoryResultRepository
 * 
 * In-memory implementation of IResultRepository.
 * Stores data in Map structure with UUID generation.
 */

import { v4 as uuidv4 } from 'uuid';
import { Result } from '@core/racing/domain/entities/result/Result';
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import type { Logger } from '@core/shared/application';

export class InMemoryResultRepository implements IResultRepository {
  private results: Map<string, Result>;
  private raceRepository: IRaceRepository | null;
  private readonly logger: Logger;

  constructor(logger: Logger, raceRepository?: IRaceRepository | null) {
    this.logger = logger;
    this.logger.info('[InMemoryResultRepository] Initialized.');
    this.results = new Map();
    this.raceRepository = raceRepository ?? null;
  }

  async findById(id: string): Promise<Result | null> {
    this.logger.debug(`[InMemoryResultRepository] Finding result by id: ${id}`);
    try {
      const result = this.results.get(id) ?? null;
      if (result) {
        this.logger.info(`[InMemoryResultRepository] Found result with id: ${id}.`);
      } else {
        this.logger.warn(`[InMemoryResultRepository] Result with id ${id} not found.`);
      }
      return result;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error finding result by id ${id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findAll(): Promise<Result[]> {
    this.logger.debug('[InMemoryResultRepository] Finding all results.');
    try {
      const results = Array.from(this.results.values());
      this.logger.info(`[InMemoryResultRepository] Found ${results.length} results.`);
      return results;
    } catch (error) {
      this.logger.error('[InMemoryResultRepository] Error finding all results:', error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findByRaceId(raceId: string): Promise<Result[]> {
    this.logger.debug(`[InMemoryResultRepository] Finding results for race id: ${raceId}`);
    try {
      const results = Array.from(this.results.values())
        .filter(result => result.raceId.toString() === raceId)
        .sort((a, b) => a.position.toNumber() - b.position.toNumber());
      this.logger.info(`[InMemoryResultRepository] Found ${results.length} results for race id: ${raceId}.`);
      return results;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error finding results for race id ${raceId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findByDriverId(driverId: string): Promise<Result[]> {
    this.logger.debug(`[InMemoryResultRepository] Finding results for driver id: ${driverId}`);
    try {
      const results = Array.from(this.results.values())
        .filter(result => result.driverId.toString() === driverId);
      this.logger.info(`[InMemoryResultRepository] Found ${results.length} results for driver id: ${driverId}.`);
      return results;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error finding results for driver id ${driverId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findByDriverIdAndLeagueId(driverId: string, leagueId: string): Promise<Result[]> {
    this.logger.debug(`[InMemoryResultRepository] Finding results for driver id: ${driverId} and league id: ${leagueId}`);
    try {
      if (!this.raceRepository) {
        this.logger.warn('[InMemoryResultRepository] Race repository not provided to InMemoryResultRepository. Skipping league-filtered search.');
        return [];
      }

      const leagueRaces = await this.raceRepository.findByLeagueId(leagueId);
      const leagueRaceIds = new Set(leagueRaces.map(race => race.id));
      this.logger.debug(`[InMemoryResultRepository] Found ${leagueRaces.length} races in league ${leagueId}.`);

      const results = Array.from(this.results.values())
        .filter(result =>
          result.driverId.toString() === driverId &&
          leagueRaceIds.has(result.raceId.toString())
        );
      this.logger.info(`[InMemoryResultRepository] Found ${results.length} results for driver ${driverId} in league ${leagueId}.`);
      return results;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error finding results for driver ${driverId} and league ${leagueId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async create(result: Result): Promise<Result> {
    this.logger.debug(`[InMemoryResultRepository] Creating result: ${result.id}`);
    try {
      if (await this.exists(result.id)) {
        this.logger.warn(`[InMemoryResultRepository] Result with ID ${result.id} already exists. Throwing error.`);
        throw new Error(`Result with ID ${result.id} already exists`);
      }

      this.results.set(result.id, result);
      this.logger.info(`[InMemoryResultRepository] Result ${result.id} created successfully.`);
      return result;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error creating result ${result.id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async createMany(results: Result[]): Promise<Result[]> {
    this.logger.debug(`[InMemoryResultRepository] Creating ${results.length} results.`);
    try {
      const created: Result[] = [];

      for (const result of results) {
        if (await this.exists(result.id)) {
          this.logger.warn(`[InMemoryResultRepository] Result with ID ${result.id} already exists. Skipping creation.`);
          // In a real system, decide if this should throw or log and skip
          continue;
        }
        this.results.set(result.id, result);
        created.push(result);
      }
      this.logger.info(`[InMemoryResultRepository] Created ${created.length} results successfully.`);

      return created;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error creating many results:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async update(result: Result): Promise<Result> {
    this.logger.debug(`[InMemoryResultRepository] Updating result: ${result.id}`);
    try {
      if (!await this.exists(result.id)) {
        this.logger.warn(`[InMemoryResultRepository] Result with ID ${result.id} not found for update. Throwing error.`);
        throw new Error(`Result with ID ${result.id} not found`);
      }

      this.results.set(result.id, result);
      this.logger.info(`[InMemoryResultRepository] Result ${result.id} updated successfully.`);
      return result;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error updating result ${result.id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async delete(id: string): Promise<void> {
    this.logger.debug(`[InMemoryResultRepository] Deleting result: ${id}`);
    try {
      if (!await this.exists(id)) {
        this.logger.warn(`[InMemoryResultRepository] Result with ID ${id} not found for deletion. Throwing error.`);
        throw new Error(`Result with ID ${id} not found`);
      }

      this.results.delete(id);
      this.logger.info(`[InMemoryResultRepository] Result ${id} deleted successfully.`);
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error deleting result ${id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async deleteByRaceId(raceId: string): Promise<void> {
    this.logger.debug(`[InMemoryResultRepository] Deleting results for race id: ${raceId}`);
    try {
      const raceResults = Array.from(this.results.values()).filter(
        result => result.raceId.toString() === raceId
      );
      raceResults.forEach(result => {
        this.results.delete(result.id);
      });
      this.logger.info(`[InMemoryResultRepository] Deleted ${raceResults.length} results for race id: ${raceId}.`);
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error deleting results for race id ${raceId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async exists(id: string): Promise<boolean> {
    this.logger.debug(`[InMemoryResultRepository] Checking existence of result with id: ${id}`);
    try {
      const exists = this.results.has(id);
      this.logger.debug(`[InMemoryResultRepository] Result ${id} exists: ${exists}.`);
      return exists;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error checking existence of result with id ${id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async existsByRaceId(raceId: string): Promise<boolean> {
    this.logger.debug(`[InMemoryResultRepository] Checking existence of results for race id: ${raceId}`);
    try {
      const exists = Array.from(this.results.values()).some(
        result => result.raceId.toString() === raceId
      );
      this.logger.debug(`[InMemoryResultRepository] Results for race ${raceId} exist: ${exists}.`);
      return exists;
    } catch (error) {
      this.logger.error(`[InMemoryResultRepository] Error checking existence of results for race id ${raceId}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  /**
   * Utility method to generate a new UUID
   */
  static generateId(): string {
    return uuidv4();
  }
}