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

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

import { PenaltyOrmEntity, ProtestOrmEntity } from '../entities/MissingRacingOrmEntities';
import { TypeOrmPersistenceSchemaError } from '../errors/TypeOrmPersistenceSchemaError';
import { PenaltyOrmMapper, ProtestOrmMapper } from './StewardingOrmMappers';

describe('PenaltyOrmMapper', () => {
  it('toDomain uses rehydrate semantics (does not call create)', () => {
    const mapper = new PenaltyOrmMapper();

    const entity = new PenaltyOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.leagueId = '00000000-0000-4000-8000-000000000002';
    entity.raceId = 'race-1';
    entity.driverId = '00000000-0000-4000-8000-000000000003';
    entity.type = 'warning';
    entity.value = null;
    entity.reason = 'Unsafe rejoin';
    entity.protestId = null;
    entity.issuedBy = '00000000-0000-4000-8000-000000000004';
    entity.status = 'pending';
    entity.issuedAt = new Date('2025-01-01T00:00:00.000Z');
    entity.appliedAt = null;
    entity.notes = null;

    const rehydrateSpy = vi.spyOn(Penalty, 'rehydrate');
    const createSpy = vi.spyOn(Penalty, 'create').mockImplementation(() => {
      throw new Error('create-called');
    });

    const domain = mapper.toDomain(entity);

    expect(domain.id.toString()).toBe(entity.id);
    expect(createSpy).not.toHaveBeenCalled();
    expect(rehydrateSpy).toHaveBeenCalled();
  });

  it('toDomain validates persisted enum values and throws adapter-scoped base schema error type', () => {
    const mapper = new PenaltyOrmMapper();

    const entity = new PenaltyOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.leagueId = '00000000-0000-4000-8000-000000000002';
    entity.raceId = 'race-1';
    entity.driverId = '00000000-0000-4000-8000-000000000003';
    entity.type = 'not-a-penalty-type';
    entity.value = null;
    entity.reason = 'Reason';
    entity.protestId = null;
    entity.issuedBy = '00000000-0000-4000-8000-000000000004';
    entity.status = 'pending';
    entity.issuedAt = new Date('2025-01-01T00:00:00.000Z');
    entity.appliedAt = null;
    entity.notes = null;

    try {
      mapper.toDomain(entity);
      throw new Error('expected-to-throw');
    } catch (error) {
      expect(error).toBeInstanceOf(TypeOrmPersistenceSchemaError);
      expect(error).toMatchObject({
        entityName: 'Penalty',
        fieldName: 'type',
        reason: 'invalid_enum_value',
      });
    }
  });
});

describe('ProtestOrmMapper', () => {
  it('toDomain uses rehydrate semantics (does not call create)', () => {
    const mapper = new ProtestOrmMapper();

    const entity = new ProtestOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.raceId = 'race-1';
    entity.protestingDriverId = '00000000-0000-4000-8000-000000000002';
    entity.accusedDriverId = '00000000-0000-4000-8000-000000000003';
    entity.incident = { lap: 1, description: 'Contact', timeInRace: 12.3 };
    entity.comment = null;
    entity.proofVideoUrl = null;
    entity.status = 'pending';
    entity.reviewedBy = null;
    entity.decisionNotes = null;
    entity.filedAt = new Date('2025-01-01T00:00:00.000Z');
    entity.reviewedAt = null;
    entity.defense = null;
    entity.defenseRequestedAt = null;
    entity.defenseRequestedBy = null;

    const rehydrateSpy = vi.spyOn(Protest, 'rehydrate');
    const createSpy = vi.spyOn(Protest, 'create').mockImplementation(() => {
      throw new Error('create-called');
    });

    const domain = mapper.toDomain(entity);

    expect(domain.id.toString()).toBe(entity.id);
    expect(createSpy).not.toHaveBeenCalled();
    expect(rehydrateSpy).toHaveBeenCalled();
  });

  it('toDomain validates persisted defense schema and throws adapter-scoped base schema error type', () => {
    const mapper = new ProtestOrmMapper();

    const entity = new ProtestOrmEntity();
    entity.id = '00000000-0000-4000-8000-000000000001';
    entity.raceId = 'race-1';
    entity.protestingDriverId = '00000000-0000-4000-8000-000000000002';
    entity.accusedDriverId = '00000000-0000-4000-8000-000000000003';
    entity.incident = { lap: 1, description: 'Contact' };
    entity.comment = null;
    entity.proofVideoUrl = null;
    entity.status = 'pending';
    entity.reviewedBy = null;
    entity.decisionNotes = null;
    entity.filedAt = new Date('2025-01-01T00:00:00.000Z');
    entity.reviewedAt = null;
    entity.defense = { statement: 'hi', submittedAt: 'not-iso' };
    entity.defenseRequestedAt = null;
    entity.defenseRequestedBy = null;

    try {
      mapper.toDomain(entity);
      throw new Error('expected-to-throw');
    } catch (error) {
      expect(error).toBeInstanceOf(TypeOrmPersistenceSchemaError);
      expect(error).toMatchObject({
        entityName: 'Protest',
        fieldName: 'defense.submittedAt',
        reason: 'not_iso_date',
      });
    }
  });
});