import 'reflect-metadata';

import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { DataSource } from 'typeorm';

import { Team } from '@core/racing/domain/entities/Team';
import { Penalty } from '@core/racing/domain/entities/penalty/Penalty';
import { Protest } from '@core/racing/domain/entities/Protest';

import {
  PenaltyOrmEntity,
  ProtestOrmEntity,
} from '../../../../../../adapters/racing/persistence/typeorm/entities/MissingRacingOrmEntities';
import {
  TeamJoinRequestOrmEntity,
  TeamMembershipOrmEntity,
  TeamOrmEntity,
} from '../../../../../../adapters/racing/persistence/typeorm/entities/TeamOrmEntities';

import { TeamMembershipOrmMapper, TeamOrmMapper } from '../../../../../../adapters/racing/persistence/typeorm/mappers/TeamOrmMappers';
import { PenaltyOrmMapper, ProtestOrmMapper } from '../../../../../../adapters/racing/persistence/typeorm/mappers/StewardingOrmMappers';

import {
  TypeOrmTeamMembershipRepository,
  TypeOrmTeamRepository,
} from '../../../../../../adapters/racing/persistence/typeorm/repositories/TeamTypeOrmRepositories';
import {
  TypeOrmPenaltyRepository,
  TypeOrmProtestRepository,
} from '../../../../../../adapters/racing/persistence/typeorm/repositories/StewardingTypeOrmRepositories';

const databaseUrl = process.env.DATABASE_URL;
const describeIfDatabase = databaseUrl ? describe : describe.skip;

describeIfDatabase('TypeORM Racing repositories (teams + stewarding slice)', () => {
  let dataSource: DataSource;

  beforeAll(async () => {
    if (!databaseUrl) {
      throw new Error('DATABASE_URL is required to run postgres integration tests');
    }

    dataSource = new DataSource({
      type: 'postgres',
      url: databaseUrl,
      entities: [
        TeamOrmEntity,
        TeamMembershipOrmEntity,
        TeamJoinRequestOrmEntity,
        PenaltyOrmEntity,
        ProtestOrmEntity,
      ],
      synchronize: true,
    });

    await dataSource.initialize();
  });

  afterAll(async () => {
    if (dataSource?.isInitialized) {
      await dataSource.destroy();
    }
  });

  it('supports: create team + membership + join request + stewarding records', async () => {
    const teamRepo = new TypeOrmTeamRepository(
      dataSource.getRepository(TeamOrmEntity),
      new TeamOrmMapper(),
    );
    const teamMembershipRepo = new TypeOrmTeamMembershipRepository(
      dataSource.getRepository(TeamMembershipOrmEntity),
      dataSource.getRepository(TeamJoinRequestOrmEntity),
      new TeamMembershipOrmMapper(),
    );

    const penaltyRepo = new TypeOrmPenaltyRepository(
      dataSource.getRepository(PenaltyOrmEntity),
      new PenaltyOrmMapper(),
    );
    const protestRepo = new TypeOrmProtestRepository(
      dataSource.getRepository(ProtestOrmEntity),
      new ProtestOrmMapper(),
    );

    const leagueId = '00000000-0000-4000-8000-000000000010';

    const team = Team.create({
      id: '00000000-0000-4000-8000-000000000001',
      name: 'Integration Team',
      tag: 'INT',
      description: 'Team for integration tests',
      ownerId: '00000000-0000-4000-8000-000000000002',
      leagues: [leagueId],
      createdAt: new Date('2025-01-01T00:00:00.000Z'),
    });

    await teamRepo.create(team);

    const membership = {
      teamId: team.id,
      driverId: '00000000-0000-4000-8000-000000000003',
      role: 'driver' as const,
      status: 'active' as const,
      joinedAt: new Date('2025-01-01T00:00:00.000Z'),
    };

    await teamMembershipRepo.saveMembership(membership);

    const joinRequest = {
      id: 'join-req-1',
      teamId: team.id,
      driverId: '00000000-0000-4000-8000-000000000004',
      requestedAt: new Date('2025-01-02T00:00:00.000Z'),
      message: 'Please let me in',
    };

    await teamMembershipRepo.saveJoinRequest(joinRequest);

    const byLeague = await teamRepo.findByLeagueId(leagueId);
    expect(byLeague.map((t) => t.id)).toContain(team.id);

    const members = await teamMembershipRepo.getTeamMembers(team.id);
    expect(members).toHaveLength(1);
    expect(members[0]?.driverId).toBe(membership.driverId);

    const requests = await teamMembershipRepo.getJoinRequests(team.id);
    expect(requests.map((r) => r.id)).toContain('join-req-1');

    const raceId = '00000000-0000-4000-8000-000000000020';

    const protest = Protest.create({
      id: '00000000-0000-4000-8000-000000000030',
      raceId,
      protestingDriverId: membership.driverId,
      accusedDriverId: '00000000-0000-4000-8000-000000000005',
      incident: { lap: 1, description: 'Contact' },
      status: 'pending',
      filedAt: new Date('2025-01-03T00:00:00.000Z'),
    });

    await protestRepo.create(protest);

    const penalty = Penalty.rehydrate({
      id: '00000000-0000-4000-8000-000000000040',
      leagueId,
      raceId,
      driverId: membership.driverId,
      type: 'warning',
      reason: 'Unsafe rejoin',
      issuedBy: '00000000-0000-4000-8000-000000000006',
      status: 'pending',
      issuedAt: new Date('2025-01-04T00:00:00.000Z'),
      protestId: protest.id,
    });

    await penaltyRepo.create(penalty);

    const penaltiesForRace = await penaltyRepo.findByRaceId(raceId);
    expect(penaltiesForRace.map((p) => p.id)).toContain(penalty.id);

    const protestsForRace = await protestRepo.findByRaceId(raceId);
    expect(protestsForRace.map((p) => p.id)).toContain(protest.id);
  });
});