import { describe, expect, it } from 'vitest';

import { League } from '@core/racing/domain/entities/League';
import { LeagueScoringConfig } from '@core/racing/domain/entities/LeagueScoringConfig';
import { Race } from '@core/racing/domain/entities/Race';
import { Season } from '@core/racing/domain/entities/season/Season';
import { PointsTable } from '@core/racing/domain/value-objects/PointsTable';
import { SessionType as RaceSessionType } from '@core/racing/domain/value-objects/SessionType';

import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
import type { SessionType } from '@core/racing/domain/types/SessionType';

import { ChampionshipConfigJsonMapper } from '@adapters/racing/persistence/typeorm/mappers/ChampionshipConfigJsonMapper';
import { LeagueOrmMapper } from '@adapters/racing/persistence/typeorm/mappers/LeagueOrmMapper';
import { LeagueScoringConfigOrmMapper } from '@adapters/racing/persistence/typeorm/mappers/LeagueScoringConfigOrmMapper';
import { PointsTableJsonMapper } from '@adapters/racing/persistence/typeorm/mappers/PointsTableJsonMapper';
import { RaceOrmMapper } from '@adapters/racing/persistence/typeorm/mappers/RaceOrmMapper';
import { SeasonOrmMapper } from '@adapters/racing/persistence/typeorm/mappers/SeasonOrmMapper';

describe('RacingOrmMappers', () => {
  it('maps League domain <-> orm', () => {
    const league = League.create({
      id: 'league-1',
      name: 'My League',
      description: 'A description',
      ownerId: 'driver-1',
      settings: {
        pointsSystem: 'custom',
        maxDrivers: 48,
        visibility: 'unranked',
      },
      socialLinks: {
        discordUrl: 'https://discord.gg/example',
      },
      participantCount: 12,
      createdAt: new Date('2025-01-01T00:00:00.000Z'),
    });

    const mapper = new LeagueOrmMapper();
    const orm = mapper.toOrmEntity(league);
    const rehydrated = mapper.toDomain(orm);

    expect(rehydrated.id.toString()).toBe('league-1');
    expect(rehydrated.name.toString()).toBe('My League');
    expect(rehydrated.description.toString()).toBe('A description');
    expect(rehydrated.ownerId.toString()).toBe('driver-1');
    expect(rehydrated.settings.maxDrivers).toBe(48);
    expect(rehydrated.settings.visibility).toBe('unranked');
    expect(rehydrated.getParticipantCount()).toBe(12);
    expect(rehydrated.socialLinks?.discordUrl).toBe('https://discord.gg/example');
  });

  it('maps Season domain <-> orm', () => {
    const season = Season.create({
      id: 'season-1',
      leagueId: 'league-1',
      gameId: 'iracing',
      name: 'Season 1',
      year: 2025,
      order: 1,
      status: 'active',
      startDate: new Date('2025-01-01T00:00:00.000Z'),
      endDate: new Date('2025-02-01T00:00:00.000Z'),
      schedulePublished: true,
      participantCount: 7,
      maxDrivers: 32,
    });

    const mapper = new SeasonOrmMapper();
    const orm = mapper.toOrmEntity(season);
    const rehydrated = mapper.toDomain(orm);

    expect(rehydrated.id).toBe('season-1');
    expect(rehydrated.leagueId).toBe('league-1');
    expect(rehydrated.gameId).toBe('iracing');
    expect(rehydrated.name).toBe('Season 1');
    expect(rehydrated.status.toString()).toBe('active');
    expect(rehydrated.schedulePublished).toBe(true);
    expect(rehydrated.getParticipantCount()).toBe(7);
  });

  it('maps Race domain <-> orm', () => {
    const race = Race.create({
      id: 'race-1',
      leagueId: 'league-1',
      scheduledAt: new Date('2025-01-10T12:00:00.000Z'),
      track: 'Spa',
      car: 'GT3',
      sessionType: RaceSessionType.main(),
      status: 'scheduled',
      registeredCount: 3,
      maxParticipants: 50,
    });

    const mapper = new RaceOrmMapper();
    const orm = mapper.toOrmEntity(race);
    const rehydrated = mapper.toDomain(orm);

    expect(rehydrated.id).toBe('race-1');
    expect(rehydrated.leagueId).toBe('league-1');
    expect(rehydrated.track).toBe('Spa');
    expect(rehydrated.car).toBe('GT3');
    expect(rehydrated.sessionType.props).toBe('main');
    expect(rehydrated.status.toString()).toBe('scheduled');
    expect(rehydrated.getRegisteredCount()).toBe(3);
    expect(rehydrated.getMaxParticipants()).toBe(50);
  });

  it('maps LeagueScoringConfig domain <-> orm (including PointsTable)', () => {
    const pointsTableBySessionType: Record<SessionType, PointsTable> = {
      practice: new PointsTable({}),
      qualifying: new PointsTable({}),
      q1: new PointsTable({}),
      q2: new PointsTable({}),
      q3: new PointsTable({}),
      sprint: new PointsTable({}),
      main: new PointsTable({ 1: 25, 2: 18 }),
      timeTrial: new PointsTable({}),
    };

    const bonusRulesBySessionType = {
      practice: [],
      qualifying: [],
      q1: [],
      q2: [],
      q3: [],
      sprint: [],
      main: [],
      timeTrial: [],
    };

    const championship: ChampionshipConfig = {
      id: 'champ-1',
      name: 'Driver Championship',
      type: 'driver',
      sessionTypes: ['main' as SessionType],
      pointsTableBySessionType,
      bonusRulesBySessionType,
      dropScorePolicy: { strategy: 'none' },
    };

    const config = LeagueScoringConfig.create({
      id: 'lsc-season-1',
      seasonId: 'season-1',
      scoringPresetId: 'club-default',
      championships: [championship],
    });

    const pointsTableMapper = new PointsTableJsonMapper();
    const championshipMapper = new ChampionshipConfigJsonMapper(pointsTableMapper);
    const mapper = new LeagueScoringConfigOrmMapper(championshipMapper);

    const orm = mapper.toOrmEntity(config);
    const rehydrated = mapper.toDomain(orm);

    expect(rehydrated.id.toString()).toBe('lsc-season-1');
    expect(rehydrated.seasonId.toString()).toBe('season-1');
    expect(rehydrated.scoringPresetId?.toString()).toBe('club-default');
    expect(rehydrated.championships).toHaveLength(1);

    const mainPointsTable =
      rehydrated.championships[0]?.pointsTableBySessionType['main' as SessionType];
    expect(mainPointsTable).toBeDefined();
    expect(mainPointsTable!.getPointsForPosition(1)).toBe(25);
  });
});