import type { DataSource } from 'typeorm';

import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import { Race, type RaceStatusValue } from '@core/racing/domain/entities/Race';

import { RaceOrmEntity } from '../entities/RaceOrmEntity';
import { RaceOrmMapper } from '../mappers/RaceOrmMapper';

export class TypeOrmRaceRepository implements IRaceRepository {
  constructor(
    private readonly dataSource: DataSource,
    private readonly mapper: RaceOrmMapper,
  ) {}

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

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

  async findByLeagueId(leagueId: string): Promise<Race[]> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    const entities = await repo.find({ where: { leagueId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findUpcomingByLeagueId(leagueId: string): Promise<Race[]> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    const entities = await repo
      .createQueryBuilder('race')
      .where('race.leagueId = :leagueId', { leagueId })
      .andWhere('race.status = :status', { status: 'scheduled' })
      .andWhere('race.scheduledAt >= NOW()')
      .getMany();
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findCompletedByLeagueId(leagueId: string): Promise<Race[]> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    const entities = await repo.find({ where: { leagueId, status: 'completed' } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findByStatus(status: RaceStatusValue): Promise<Race[]> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    const entities = await repo.find({ where: { status } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findByDateRange(startDate: Date, endDate: Date): Promise<Race[]> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    const entities = await repo
      .createQueryBuilder('race')
      .where('race.scheduledAt >= :startDate', { startDate })
      .andWhere('race.scheduledAt <= :endDate', { endDate })
      .getMany();
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async create(race: Race): Promise<Race> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    await repo.save(this.mapper.toOrmEntity(race));
    return race;
  }

  async update(race: Race): Promise<Race> {
    const repo = this.dataSource.getRepository(RaceOrmEntity);
    await repo.save(this.mapper.toOrmEntity(race));
    return race;
  }

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

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