import { Test, TestingModule } from '@nestjs/testing';
import { AppVersionMapper } from '../../../../src/infrastructure/persistence/mappers/app-version.mapper';
import { AppVersion } from '../../../../src/domain/entities/app-version.entity';
import { Platform } from '../../../../src/domain/enums/platform.enum';
import { Platform as PrismaPlatform } from '@prisma/client';
import { VersionCode } from '../../../../src/domain/value-objects/version-code.vo';
import { VersionName } from '../../../../src/domain/value-objects/version-name.vo';
import { BuildNumber } from '../../../../src/domain/value-objects/build-number.vo';
import { DownloadUrl } from '../../../../src/domain/value-objects/download-url.vo';
import { FileSize } from '../../../../src/domain/value-objects/file-size.vo';
import { FileSha256 } from '../../../../src/domain/value-objects/file-sha256.vo';
import { Changelog } from '../../../../src/domain/value-objects/changelog.vo';

describe('AppVersionMapper', () => {
  let mapper: AppVersionMapper;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [AppVersionMapper],
    }).compile();

    mapper = module.get<AppVersionMapper>(AppVersionMapper);
  });

  const createDomainVersion = () => {
    return AppVersion.create({
      platform: Platform.ANDROID,
      versionCode: VersionCode.create(100),
      versionName: VersionName.create('1.0.0'),
      buildNumber: BuildNumber.create('100'),
      downloadUrl: DownloadUrl.create('https://example.com/app.apk'),
      fileSize: FileSize.create(10485760n),
      fileSha256: FileSha256.create('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'),
      changelog: Changelog.create('Initial release'),
      isForceUpdate: false,
      createdBy: 'admin',
    });
  };

  const createPrismaVersion = () => ({
    id: 'test-id',
    platform: PrismaPlatform.ANDROID,
    versionCode: 100,
    versionName: '1.0.0',
    buildNumber: '100',
    downloadUrl: 'https://example.com/app.apk',
    fileSize: 10485760n,
    fileSha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
    changelog: 'Initial release',
    minOsVersion: null,
    isEnabled: true,
    isForceUpdate: false,
    releaseDate: null,
    createdAt: new Date('2024-01-01'),
    updatedAt: new Date('2024-01-01'),
    createdBy: 'admin',
    updatedBy: null,
  });

  describe('toDomain', () => {
    it('should map Prisma model to domain entity', () => {
      const prismaVersion = createPrismaVersion();
      const domainVersion = mapper.toDomain(prismaVersion);

      expect(domainVersion).toBeInstanceOf(AppVersion);
      expect(domainVersion.id).toBe(prismaVersion.id);
      expect(domainVersion.platform).toBe(Platform.ANDROID);
      expect(domainVersion.versionCode.value).toBe(100);
      expect(domainVersion.versionName.value).toBe('1.0.0');
      expect(domainVersion.buildNumber.value).toBe('100');
      expect(domainVersion.downloadUrl.value).toBe('https://example.com/app.apk');
      expect(domainVersion.fileSize.bytes).toBe(10485760n);
      expect(domainVersion.fileSha256.value).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
      expect(domainVersion.changelog.value).toBe('Initial release');
      expect(domainVersion.isEnabled).toBe(true);
      expect(domainVersion.isForceUpdate).toBe(false);
    });

    it('should map iOS platform correctly', () => {
      const prismaVersion = { ...createPrismaVersion(), platform: PrismaPlatform.IOS };
      const domainVersion = mapper.toDomain(prismaVersion);

      expect(domainVersion.platform).toBe(Platform.IOS);
    });

    it('should handle null minOsVersion', () => {
      const prismaVersion = { ...createPrismaVersion(), minOsVersion: null };
      const domainVersion = mapper.toDomain(prismaVersion);

      expect(domainVersion.minOsVersion).toBeNull();
    });

    it('should handle null releaseDate', () => {
      const prismaVersion = { ...createPrismaVersion(), releaseDate: null };
      const domainVersion = mapper.toDomain(prismaVersion);

      expect(domainVersion.releaseDate).toBeNull();
    });
  });

  describe('toPersistence', () => {
    it('should map domain entity to Prisma model', () => {
      const domainVersion = createDomainVersion();
      const persistenceModel = mapper.toPersistence(domainVersion);

      expect(persistenceModel.id).toBe(domainVersion.id);
      expect(persistenceModel.platform).toBe(Platform.ANDROID);
      expect(persistenceModel.versionCode).toBe(100);
      expect(persistenceModel.versionName).toBe('1.0.0');
      expect(persistenceModel.buildNumber).toBe('100');
      expect(persistenceModel.downloadUrl).toBe('https://example.com/app.apk');
      expect(persistenceModel.fileSize).toBe(10485760n);
      expect(persistenceModel.fileSha256).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
      expect(persistenceModel.changelog).toBe('Initial release');
      expect(persistenceModel.isEnabled).toBe(true);
      expect(persistenceModel.isForceUpdate).toBe(false);
    });

    it('should exclude createdAt and updatedAt from output', () => {
      const domainVersion = createDomainVersion();
      const persistenceModel = mapper.toPersistence(domainVersion);

      expect(persistenceModel).not.toHaveProperty('createdAt');
      expect(persistenceModel).not.toHaveProperty('updatedAt');
    });
  });

  describe('round-trip conversion', () => {
    it('should maintain data integrity through round-trip conversion', () => {
      const originalPrisma = createPrismaVersion();
      const domain = mapper.toDomain(originalPrisma);
      const backToPersistence = mapper.toPersistence(domain);

      expect(backToPersistence.id).toBe(originalPrisma.id);
      expect(backToPersistence.versionCode).toBe(originalPrisma.versionCode);
      expect(backToPersistence.versionName).toBe(originalPrisma.versionName);
      expect(backToPersistence.platform).toBe(originalPrisma.platform);
    });
  });
});
