import { DriverLivery } from '@core/racing/domain/entities/DriverLivery';
import { LiveryTemplate } from '@core/racing/domain/entities/LiveryTemplate';
import { LiveryDecal } from '@core/racing/domain/value-objects/LiveryDecal';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { InMemoryLiveryRepository } from './InMemoryLiveryRepository';

describe('InMemoryLiveryRepository', () => {
  let repository: InMemoryLiveryRepository;
  let mockLogger: Logger;

  beforeEach(() => {
    mockLogger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
    repository = new InMemoryLiveryRepository(mockLogger);
  });

  const createTestDriverLivery = (id: string, driverId: string, gameId: string, carId: string, uploadedImageUrl: string) => {
    return DriverLivery.create({
      id,
      driverId,
      gameId,
      carId,
      uploadedImageUrl,
    });
  };

  const createTestLiveryTemplate = (id: string, leagueId: string, seasonId: string, carId: string, baseImageUrl: string) => {
    return LiveryTemplate.create({
      id,
      leagueId,
      seasonId,
      carId,
      baseImageUrl,
    });
  };

  const createTestDecal = (id: string, type: 'user' | 'sponsor', imageUrl: string) => {
    return LiveryDecal.create({
      id,
      type,
      imageUrl,
      x: 0.5,
      y: 0.5,
      width: 0.1,
      height: 0.1,
      zIndex: 1,
    });
  };

  describe('constructor', () => {
    it('should initialize with a logger', () => {
      expect(repository).toBeDefined();
      expect(mockLogger.info).toHaveBeenCalledWith('InMemoryLiveryRepository initialized.');
    });
  });

  describe('DriverLivery operations', () => {
    describe('findDriverLiveryById', () => {
      it('should return null if driver livery not found', async () => {
        const result = await repository.findDriverLiveryById('nonexistent');
        expect(result).toBeNull();
        expect(mockLogger.debug).toHaveBeenCalledWith('Finding driver livery by id: nonexistent');
        expect(mockLogger.warn).toHaveBeenCalledWith('Driver livery with id nonexistent not found.');
      });

      it('should return the driver livery if found', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        await repository.createDriverLivery(livery);

        const result = await repository.findDriverLiveryById('1');
        expect(result).toEqual(livery);
        expect(mockLogger.info).toHaveBeenCalledWith('Found driver livery: 1.');
      });
    });

    describe('findDriverLiveriesByDriverId', () => {
      it('should return driver liveries for the given driver', async () => {
        const livery1 = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image1.jpg');
        const livery2 = createTestDriverLivery('2', 'driver1', 'game2', 'car2', 'http://example.com/image2.jpg');
        const livery3 = createTestDriverLivery('3', 'driver2', 'game1', 'car1', 'http://example.com/image3.jpg');
        await repository.createDriverLivery(livery1);
        await repository.createDriverLivery(livery2);
        await repository.createDriverLivery(livery3);

        const result = await repository.findDriverLiveriesByDriverId('driver1');
        expect(result).toHaveLength(2);
        expect(result).toContain(livery1);
        expect(result).toContain(livery2);
      });
    });

    describe('findDriverLiveryByDriverAndCar', () => {
      it('should return null if no livery found', async () => {
        const result = await repository.findDriverLiveryByDriverAndCar('driver1', 'car1');
        expect(result).toBeNull();
      });

      it('should return the livery if found', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        await repository.createDriverLivery(livery);

        const result = await repository.findDriverLiveryByDriverAndCar('driver1', 'car1');
        expect(result).toEqual(livery);
      });
    });

    describe('findDriverLiveriesByGameId', () => {
      it('should return driver liveries for the given game', async () => {
        const livery1 = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image1.jpg');
        const livery2 = createTestDriverLivery('2', 'driver2', 'game1', 'car2', 'http://example.com/image2.jpg');
        const livery3 = createTestDriverLivery('3', 'driver1', 'game2', 'car1', 'http://example.com/image3.jpg');
        await repository.createDriverLivery(livery1);
        await repository.createDriverLivery(livery2);
        await repository.createDriverLivery(livery3);

        const result = await repository.findDriverLiveriesByGameId('game1');
        expect(result).toHaveLength(2);
        expect(result).toContain(livery1);
        expect(result).toContain(livery2);
      });
    });

    describe('findDriverLiveryByDriverAndGame', () => {
      it('should return driver liveries for the given driver and game', async () => {
        const livery1 = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image1.jpg');
        const livery2 = createTestDriverLivery('2', 'driver1', 'game1', 'car2', 'http://example.com/image2.jpg');
        const livery3 = createTestDriverLivery('3', 'driver1', 'game2', 'car1', 'http://example.com/image3.jpg');
        await repository.createDriverLivery(livery1);
        await repository.createDriverLivery(livery2);
        await repository.createDriverLivery(livery3);

        const result = await repository.findDriverLiveryByDriverAndGame('driver1', 'game1');
        expect(result).toHaveLength(2);
        expect(result).toContain(livery1);
        expect(result).toContain(livery2);
      });
    });

    describe('createDriverLivery', () => {
      it('should create a new driver livery', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        const result = await repository.createDriverLivery(livery);
        expect(result).toEqual(livery);
        expect(mockLogger.info).toHaveBeenCalledWith('DriverLivery 1 created successfully.');
      });

      it('should throw error if driver livery already exists', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        await repository.createDriverLivery(livery);
        await expect(repository.createDriverLivery(livery)).rejects.toThrow('DriverLivery with this ID already exists');
      });
    });

    describe('updateDriverLivery', () => {
      it('should update an existing driver livery', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        await repository.createDriverLivery(livery);

        const decal = createTestDecal('decal1', 'user', 'http://example.com/decal.jpg');
        const updatedLivery = livery.addDecal(decal);
        const result = await repository.updateDriverLivery(updatedLivery);
        expect(result).toEqual(updatedLivery);
        expect(mockLogger.info).toHaveBeenCalledWith('DriverLivery 1 updated successfully.');
      });

      it('should throw error if driver livery does not exist', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        await expect(repository.updateDriverLivery(livery)).rejects.toThrow('DriverLivery not found');
      });
    });

    describe('deleteDriverLivery', () => {
      it('should delete an existing driver livery', async () => {
        const livery = createTestDriverLivery('1', 'driver1', 'game1', 'car1', 'http://example.com/image.jpg');
        await repository.createDriverLivery(livery);

        await repository.deleteDriverLivery('1');
        expect(mockLogger.info).toHaveBeenCalledWith('DriverLivery 1 deleted successfully.');
        const found = await repository.findDriverLiveryById('1');
        expect(found).toBeNull();
      });

      it('should not throw error if driver livery does not exist', async () => {
        await repository.deleteDriverLivery('nonexistent');
        expect(mockLogger.warn).toHaveBeenCalledWith('DriverLivery with id nonexistent not found for deletion.');
      });
    });
  });

  describe('LiveryTemplate operations', () => {
    describe('findTemplateById', () => {
      it('should return null if template not found', async () => {
        const result = await repository.findTemplateById('nonexistent');
        expect(result).toBeNull();
        expect(mockLogger.warn).toHaveBeenCalledWith('Livery template with id nonexistent not found.');
      });

      it('should return the template if found', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        await repository.createTemplate(template);

        const result = await repository.findTemplateById('1');
        expect(result).toEqual(template);
        expect(mockLogger.info).toHaveBeenCalledWith('Found livery template: 1.');
      });
    });

    describe('findTemplatesBySeasonId', () => {
      it('should return templates for the given season', async () => {
        const template1 = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base1.jpg');
        const template2 = createTestLiveryTemplate('2', 'league2', 'season1', 'car2', 'http://example.com/base2.jpg');
        const template3 = createTestLiveryTemplate('3', 'league1', 'season2', 'car1', 'http://example.com/base3.jpg');
        await repository.createTemplate(template1);
        await repository.createTemplate(template2);
        await repository.createTemplate(template3);

        const result = await repository.findTemplatesBySeasonId('season1');
        expect(result).toHaveLength(2);
        expect(result).toContain(template1);
        expect(result).toContain(template2);
      });
    });

    describe('findTemplateBySeasonAndCar', () => {
      it('should return null if no template found', async () => {
        const result = await repository.findTemplateBySeasonAndCar('season1', 'car1');
        expect(result).toBeNull();
      });

      it('should return the template if found', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        await repository.createTemplate(template);

        const result = await repository.findTemplateBySeasonAndCar('season1', 'car1');
        expect(result).toEqual(template);
      });
    });

    describe('createTemplate', () => {
      it('should create a new template', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        const result = await repository.createTemplate(template);
        expect(result).toEqual(template);
        expect(mockLogger.info).toHaveBeenCalledWith('LiveryTemplate 1 created successfully.');
      });

      it('should throw error if template already exists', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        await repository.createTemplate(template);
        await expect(repository.createTemplate(template)).rejects.toThrow('LiveryTemplate with this ID already exists');
      });
    });

    describe('updateTemplate', () => {
      it('should update an existing template', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        await repository.createTemplate(template);

        const decal = createTestDecal('decal1', 'sponsor', 'http://example.com/decal.jpg');
        const updatedTemplate = template.addDecal(decal);
        const result = await repository.updateTemplate(updatedTemplate);
        expect(result).toEqual(updatedTemplate);
        expect(mockLogger.info).toHaveBeenCalledWith('LiveryTemplate 1 updated successfully.');
      });

      it('should throw error if template does not exist', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        await expect(repository.updateTemplate(template)).rejects.toThrow('LiveryTemplate not found');
      });
    });

    describe('deleteTemplate', () => {
      it('should delete an existing template', async () => {
        const template = createTestLiveryTemplate('1', 'league1', 'season1', 'car1', 'http://example.com/base.jpg');
        await repository.createTemplate(template);

        await repository.deleteTemplate('1');
        expect(mockLogger.info).toHaveBeenCalledWith('LiveryTemplate 1 deleted successfully.');
        const found = await repository.findTemplateById('1');
        expect(found).toBeNull();
      });

      it('should not throw error if template does not exist', async () => {
        await repository.deleteTemplate('nonexistent');
        expect(mockLogger.warn).toHaveBeenCalledWith('LiveryTemplate with id nonexistent not found for deletion.');
      });
    });
  });
});