import type { DataSource } from 'typeorm';
import { In } from 'typeorm';

import type { Result } from '@core/racing/domain/entities/result/Result';
import type { ResultRepository } from '@core/racing/domain/repositories/ResultRepository';

import { RaceOrmEntity } from '../entities/RaceOrmEntity';
import { ResultOrmEntity } from '../entities/ResultOrmEntity';
import { ResultOrmMapper } from '../mappers/ResultOrmMapper';

export class TypeOrmResultRepository implements ResultRepository {
  constructor(
    private readonly dataSource: DataSource,
    private readonly mapper: ResultOrmMapper,
  ) {}

  async findById(id: string): Promise<Result | null> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    const entity = await repo.findOne({ where: { id } });
    return entity ? this.mapper.toDomain(entity) : null;
  }

  async findAll(): Promise<Result[]> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    const entities = await repo.find();
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findByRaceId(raceId: string): Promise<Result[]> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    const entities = await repo.find({ where: { raceId }, order: { position: 'ASC' } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findByDriverId(driverId: string): Promise<Result[]> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    const entities = await repo.find({ where: { driverId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findByDriverIdAndLeagueId(driverId: string, leagueId: string): Promise<Result[]> {
    const raceRepo = this.dataSource.getRepository(RaceOrmEntity);
    const races = await raceRepo.find({ where: { leagueId }, select: { id: true } });
    const raceIds = races.map((r) => r.id);

    if (raceIds.length === 0) {
      return [];
    }

    const resultRepo = this.dataSource.getRepository(ResultOrmEntity);
    const entities = await resultRepo.find({ where: { driverId, raceId: In(raceIds) } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async create(result: Result): Promise<Result> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    await repo.save(this.mapper.toOrmEntity(result));
    return result;
  }

  async createMany(results: Result[]): Promise<Result[]> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    await repo.save(results.map((r) => this.mapper.toOrmEntity(r)));
    return results;
  }

  async update(result: Result): Promise<Result> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    await repo.save(this.mapper.toOrmEntity(result));
    return result;
  }

  async delete(id: string): Promise<void> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    await repo.delete({ id });
  }

  async deleteByRaceId(raceId: string): Promise<void> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    await repo.delete({ raceId });
  }

  async exists(id: string): Promise<boolean> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    const count = await repo.count({ where: { id } });
    return count > 0;
  }

  async existsByRaceId(raceId: string): Promise<boolean> {
    const repo = this.dataSource.getRepository(ResultOrmEntity);
    const count = await repo.count({ where: { raceId } });
    return count > 0;
  }
}