import type { Repository } from 'typeorm';

import type { GameRepository } from '@core/racing/domain/repositories/GameRepository';
import type { LeagueWalletRepository } from '@core/racing/domain/repositories/LeagueWalletRepository';
import type { SeasonSponsorshipRepository } from '@core/racing/domain/repositories/SeasonSponsorshipRepository';
import type { SponsorRepository } from '@core/racing/domain/repositories/SponsorRepository';
import type { SponsorshipPricingRepository } from '@core/racing/domain/repositories/SponsorshipPricingRepository';
import type { SponsorshipRequestRepository } from '@core/racing/domain/repositories/SponsorshipRequestRepository';
import type { TransactionRepository } from '@core/racing/domain/repositories/TransactionRepository';

import type { Game } from '@core/racing/domain/entities/Game';
import type { LeagueWallet } from '@core/racing/domain/entities/league-wallet/LeagueWallet';
import type { Transaction, TransactionType } from '@core/racing/domain/entities/league-wallet/Transaction';
import type { SeasonSponsorship, SponsorshipTier } from '@core/racing/domain/entities/season/SeasonSponsorship';
import type { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
import type { SponsorableEntityType, SponsorshipRequest, SponsorshipRequestStatus } from '@core/racing/domain/entities/SponsorshipRequest';
import type { SponsorshipPricing } from '@core/racing/domain/value-objects/SponsorshipPricing';

import {
    GameOrmEntity,
    LeagueWalletOrmEntity,
    SeasonSponsorshipOrmEntity,
    SponsorOrmEntity,
    SponsorshipPricingOrmEntity,
    SponsorshipRequestOrmEntity,
    TransactionOrmEntity,
} from '../entities/MissingRacingOrmEntities';
import {
    GameOrmMapper,
    LeagueWalletOrmMapper,
    SeasonSponsorshipOrmMapper,
    SponsorOrmMapper,
    SponsorshipPricingOrmMapper,
    SponsorshipRequestOrmMapper,
    TransactionOrmMapper,
} from '../mappers/CommerceOrmMappers';

export class TypeOrmGameRepository implements GameRepository {
  constructor(
    private readonly repo: Repository<GameOrmEntity>,
    private readonly mapper: GameOrmMapper,
  ) {}

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

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

export class TypeOrmLeagueWalletRepository implements LeagueWalletRepository {
  constructor(
    private readonly repo: Repository<LeagueWalletOrmEntity>,
    private readonly mapper: LeagueWalletOrmMapper,
  ) {}

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

  async findByLeagueId(leagueId: string): Promise<LeagueWallet | null> {
    const entity = await this.repo.findOne({ where: { leagueId } });
    return entity ? this.mapper.toDomain(entity) : null;
  }

  async create(wallet: LeagueWallet): Promise<LeagueWallet> {
    await this.repo.save(this.mapper.toOrmEntity(wallet));
    return wallet;
  }

  async update(wallet: LeagueWallet): Promise<LeagueWallet> {
    await this.repo.save(this.mapper.toOrmEntity(wallet));
    return wallet;
  }

  async delete(id: string): Promise<void> {
    await this.repo.delete({ id });
  }

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

export class TypeOrmTransactionRepository implements TransactionRepository {
  constructor(
    private readonly repo: Repository<TransactionOrmEntity>,
    private readonly mapper: TransactionOrmMapper,
  ) {}

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

  async findByWalletId(walletId: string): Promise<Transaction[]> {
    const entities = await this.repo.find({ where: { walletId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findByType(type: TransactionType): Promise<Transaction[]> {
    const entities = await this.repo.find({ where: { type } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async create(transaction: Transaction): Promise<Transaction> {
    await this.repo.save(this.mapper.toOrmEntity(transaction));
    return transaction;
  }

  async update(transaction: Transaction): Promise<Transaction> {
    await this.repo.save(this.mapper.toOrmEntity(transaction));
    return transaction;
  }

  async delete(id: string): Promise<void> {
    await this.repo.delete({ id });
  }

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

export class TypeOrmSponsorRepository implements SponsorRepository {
  constructor(
    private readonly repo: Repository<SponsorOrmEntity>,
    private readonly mapper: SponsorOrmMapper,
  ) {}

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

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

  async findByEmail(email: string): Promise<Sponsor | null> {
    const entity = await this.repo.findOne({ where: { contactEmail: email } });
    return entity ? this.mapper.toDomain(entity) : null;
  }

  async create(sponsor: Sponsor): Promise<Sponsor> {
    await this.repo.save(this.mapper.toOrmEntity(sponsor));
    return sponsor;
  }

  async update(sponsor: Sponsor): Promise<Sponsor> {
    await this.repo.save(this.mapper.toOrmEntity(sponsor));
    return sponsor;
  }

  async delete(id: string): Promise<void> {
    await this.repo.delete({ id });
  }

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

export class TypeOrmSponsorshipPricingRepository implements SponsorshipPricingRepository {
  constructor(
    private readonly repo: Repository<SponsorshipPricingOrmEntity>,
    private readonly mapper: SponsorshipPricingOrmMapper,
  ) {}

  async findByEntity(entityType: SponsorableEntityType, entityId: string): Promise<SponsorshipPricing | null> {
    const id = this.mapper.makeId(entityType, entityId);
    const entity = await this.repo.findOne({ where: { id } });
    return entity ? this.mapper.toDomain(entity) : null;
  }

  async save(entityType: SponsorableEntityType, entityId: string, pricing: SponsorshipPricing): Promise<void> {
    await this.repo.save(this.mapper.toOrmEntity(entityType, entityId, pricing));
  }

  async delete(entityType: SponsorableEntityType, entityId: string): Promise<void> {
    const id = this.mapper.makeId(entityType, entityId);
    await this.repo.delete({ id });
  }

  async exists(entityType: SponsorableEntityType, entityId: string): Promise<boolean> {
    const id = this.mapper.makeId(entityType, entityId);
    const count = await this.repo.count({ where: { id } });
    return count > 0;
  }

  async findAcceptingApplications(entityType: SponsorableEntityType): Promise<Array<{ entityId: string; pricing: SponsorshipPricing }>> {
    const entities = await this.repo.find({ where: { entityType, pricing: { acceptingApplications: true } } as any });
    return entities.map((e) => ({ entityId: e.entityId, pricing: this.mapper.toDomain(e) }));
  }
}

export class TypeOrmSponsorshipRequestRepository implements SponsorshipRequestRepository {
  constructor(
    private readonly repo: Repository<SponsorshipRequestOrmEntity>,
    private readonly mapper: SponsorshipRequestOrmMapper,
  ) {}

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

  async findByEntity(entityType: SponsorableEntityType, entityId: string): Promise<SponsorshipRequest[]> {
    const entities = await this.repo.find({ where: { entityType, entityId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findPendingByEntity(entityType: SponsorableEntityType, entityId: string): Promise<SponsorshipRequest[]> {
    const entities = await this.repo.find({ where: { entityType, entityId, status: 'pending' } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findBySponsorId(sponsorId: string): Promise<SponsorshipRequest[]> {
    const entities = await this.repo.find({ where: { sponsorId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

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

  async findBySponsorIdAndStatus(sponsorId: string, status: SponsorshipRequestStatus): Promise<SponsorshipRequest[]> {
    const entities = await this.repo.find({ where: { sponsorId, status } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async hasPendingRequest(sponsorId: string, entityType: SponsorableEntityType, entityId: string): Promise<boolean> {
    const count = await this.repo.count({ where: { sponsorId, entityType, entityId, status: 'pending' } });
    return count > 0;
  }

  async countPendingByEntity(entityType: SponsorableEntityType, entityId: string): Promise<number> {
    return this.repo.count({ where: { entityType, entityId, status: 'pending' } });
  }

  async create(request: SponsorshipRequest): Promise<SponsorshipRequest> {
    await this.repo.save(this.mapper.toOrmEntity(request));
    return request;
  }

  async update(request: SponsorshipRequest): Promise<SponsorshipRequest> {
    await this.repo.save(this.mapper.toOrmEntity(request));
    return request;
  }

  async delete(id: string): Promise<void> {
    await this.repo.delete({ id });
  }

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

export class TypeOrmSeasonSponsorshipRepository implements SeasonSponsorshipRepository {
  constructor(
    private readonly repo: Repository<SeasonSponsorshipOrmEntity>,
    private readonly mapper: SeasonSponsorshipOrmMapper,
  ) {}

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

  async findBySeasonId(seasonId: string): Promise<SeasonSponsorship[]> {
    const entities = await this.repo.find({ where: { seasonId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

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

  async findBySponsorId(sponsorId: string): Promise<SeasonSponsorship[]> {
    const entities = await this.repo.find({ where: { sponsorId } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async findBySeasonAndTier(seasonId: string, tier: SponsorshipTier): Promise<SeasonSponsorship[]> {
    const entities = await this.repo.find({ where: { seasonId, tier } });
    return entities.map((e) => this.mapper.toDomain(e));
  }

  async create(sponsorship: SeasonSponsorship): Promise<SeasonSponsorship> {
    await this.repo.save(this.mapper.toOrmEntity(sponsorship));
    return sponsorship;
  }

  async update(sponsorship: SeasonSponsorship): Promise<SeasonSponsorship> {
    await this.repo.save(this.mapper.toOrmEntity(sponsorship));
    return sponsorship;
  }

  async delete(id: string): Promise<void> {
    await this.repo.delete({ id });
  }

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