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

import type { DataSource } from 'typeorm';

import { TypeOrmDriverRepository } from './TypeOrmDriverRepository';
import { DriverOrmMapper } from '../mappers/DriverOrmMapper';
import { Driver } from '@core/racing/domain/entities/Driver';
import { MediaReference } from '@core/domain/media/MediaReference';

describe('TypeOrmDriverRepository', () => {
  it('constructor requires injected mapper (no internal mapper instantiation)', () => {
    const dataSource = {} as unknown as DataSource;
    const mapper = {} as unknown as DriverOrmMapper;

    const repo = new TypeOrmDriverRepository(dataSource, mapper);

    expect(repo).toBeInstanceOf(TypeOrmDriverRepository);
    expect((repo as unknown as { mapper: unknown }).mapper).toBe(mapper);
  });

  it('works with mocked TypeORM DataSource (no DB required)', async () => {
    const findOne = async () => null;

    const dataSource = {
      getRepository: () => ({ findOne }),
    } as unknown as DataSource;

    const mapper = {
      toDomain: () => {
        throw new Error('should-not-be-called');
      },
    } as unknown as DriverOrmMapper;

    const repo = new TypeOrmDriverRepository(dataSource, mapper);

    await expect(repo.findById('driver-1')).resolves.toBeNull();
  });

  it('persists and retrieves driver with avatarRef (roundtrip test)', async () => {
    // Create a driver with a specific avatar reference
    const driver = Driver.create({
      id: 'driver-123',
      iracingId: '456789',
      name: 'Test Driver',
      country: 'US',
      avatarRef: MediaReference.createUploaded('media-abc-123'),
    });

    // Mock entity that would be saved
    const mockEntity = {
      id: 'driver-123',
      iracingId: '456789',
      name: 'Test Driver',
      country: 'US',
      bio: null,
      joinedAt: driver.joinedAt.toDate(),
      category: null,
      avatarRef: { type: 'uploaded', mediaId: 'media-abc-123' },
    };

    const savedEntities: any[] = [];

    const repo = {
      save: async (entity: any) => {
        savedEntities.push(entity);
        return entity;
      },
      findOne: async () => savedEntities[0] || null,
    };

    const mapper = new DriverOrmMapper();

    const typeOrmRepo = new TypeOrmDriverRepository(
      { getRepository: () => repo } as unknown as DataSource,
      mapper,
    );

    // Test save
    await typeOrmRepo.create(driver);

    expect(savedEntities).toHaveLength(1);
    expect(savedEntities[0].avatarRef).toEqual({ type: 'uploaded', mediaId: 'media-abc-123' });

    // Test load
    const loaded = await typeOrmRepo.findById('driver-123');
    expect(loaded).not.toBeNull();
    expect(loaded!.avatarRef.type).toBe('uploaded');
    expect(loaded!.avatarRef.mediaId).toBe('media-abc-123');
  });

  it('handles system-default avatarRef correctly', async () => {
    const driver = Driver.create({
      id: 'driver-456',
      iracingId: '98765',
      name: 'Default Driver',
      country: 'UK',
      avatarRef: MediaReference.createSystemDefault('avatar'),
    });

    const mockEntity = {
      id: 'driver-456',
      iracingId: '98765',
      name: 'Default Driver',
      country: 'UK',
      bio: null,
      joinedAt: driver.joinedAt.toDate(),
      category: null,
      avatarRef: { type: 'system-default', variant: 'avatar' },
    };

    const savedEntities: any[] = [];

    const repo = {
      save: async (entity: any) => {
        savedEntities.push(entity);
        return entity;
      },
      findOne: async () => savedEntities[0] || null,
    };

    const mapper = new DriverOrmMapper();

    const typeOrmRepo = new TypeOrmDriverRepository(
      { getRepository: () => repo } as unknown as DataSource,
      mapper,
    );

    await typeOrmRepo.create(driver);

    expect(savedEntities[0].avatarRef).toEqual({ type: 'system-default', variant: 'avatar' });

    const loaded = await typeOrmRepo.findById('driver-456');
    expect(loaded!.avatarRef.type).toBe('system-default');
    expect(loaded!.avatarRef.variant).toBe('avatar');
  });

  it('handles generated avatarRef correctly', async () => {
    const driver = Driver.create({
      id: 'driver-789',
      iracingId: '11111',
      name: 'Generated Driver',
      country: 'DE',
      avatarRef: MediaReference.createGenerated('gen-req-xyz'),
    });

    const mockEntity = {
      id: 'driver-789',
      iracingId: '11111',
      name: 'Generated Driver',
      country: 'DE',
      bio: null,
      joinedAt: driver.joinedAt.toDate(),
      category: null,
      avatarRef: { type: 'generated', generationRequestId: 'gen-req-xyz' },
    };

    const savedEntities: any[] = [];

    const repo = {
      save: async (entity: any) => {
        savedEntities.push(entity);
        return entity;
      },
      findOne: async () => savedEntities[0] || null,
    };

    const mapper = new DriverOrmMapper();

    const typeOrmRepo = new TypeOrmDriverRepository(
      { getRepository: () => repo } as unknown as DataSource,
      mapper,
    );

    await typeOrmRepo.create(driver);

    expect(savedEntities[0].avatarRef).toEqual({ type: 'generated', generationRequestId: 'gen-req-xyz' });

    const loaded = await typeOrmRepo.findById('driver-789');
    expect(loaded!.avatarRef.type).toBe('generated');
    expect(loaded!.avatarRef.generationRequestId).toBe('gen-req-xyz');
  });

  it('handles update with changed avatarRef', async () => {
    const driver = Driver.create({
      id: 'driver-update',
      iracingId: '22222',
      name: 'Update Driver',
      country: 'FR',
      avatarRef: MediaReference.createSystemDefault('avatar'),
    });

    const savedEntities: any[] = [];

    const repo = {
      save: async (entity: any) => {
        savedEntities.push(entity);
        return entity;
      },
      findOne: async () => savedEntities[savedEntities.length - 1] || null,
    };

    const mapper = new DriverOrmMapper();

    const typeOrmRepo = new TypeOrmDriverRepository(
      { getRepository: () => repo } as unknown as DataSource,
      mapper,
    );

    // Initial save
    await typeOrmRepo.create(driver);
    expect(savedEntities[0].avatarRef).toEqual({ type: 'system-default', variant: 'avatar' });

    // Update with new avatar
    const updatedDriver = driver.update({
      avatarRef: MediaReference.createUploaded('new-media-id'),
    });

    await typeOrmRepo.update(updatedDriver);

    expect(savedEntities).toHaveLength(2);
    expect(savedEntities[1].avatarRef).toEqual({ type: 'uploaded', mediaId: 'new-media-id' });

    const loaded = await typeOrmRepo.findById('driver-update');
    expect(loaded!.avatarRef.type).toBe('uploaded');
    expect(loaded!.avatarRef.mediaId).toBe('new-media-id');
  });
});