import { Test, TestingModule } from '@nestjs/testing';
import { ConfigModule } from '@nestjs/config';
import { PrismaService } from '../../../src/infrastructure/persistence/prisma/prisma.service';
import { AppVersionRepositoryImpl } from '../../../src/infrastructure/persistence/repositories/app-version.repository.impl';
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 { 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('AppVersionRepository Integration Tests', () => {
  let repository: AppVersionRepositoryImpl;
  let prisma: PrismaService;

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({
          isGlobal: true,
          envFilePath: '.env.test',
        }),
      ],
      providers: [
        PrismaService,
        AppVersionMapper,
        AppVersionRepositoryImpl,
      ],
    }).compile();

    repository = module.get<AppVersionRepositoryImpl>(AppVersionRepositoryImpl);
    prisma = module.get<PrismaService>(PrismaService);

    // Connect to test database
    await prisma.$connect();
  });

  afterAll(async () => {
    await prisma.$disconnect();
  });

  beforeEach(async () => {
    // Clean up test data
    await prisma.appVersion.deleteMany({});
  });

  const createTestVersion = () => {
    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('Test version for integration testing'),
      isForceUpdate: false,
      createdBy: 'test-user',
    });
  };

  describe('save', () => {
    it('should save new app version', async () => {
      const version = createTestVersion();

      const saved = await repository.save(version);

      expect(saved.id).toBe(version.id);
      expect(saved.versionCode.value).toBe(100);
      expect(saved.versionName.value).toBe('1.0.0');
    });

    it('should save version and retrieve it from database', async () => {
      const version = createTestVersion();
      await repository.save(version);

      const found = await repository.findById(version.id);

      expect(found).not.toBeNull();
      expect(found!.id).toBe(version.id);
      expect(found!.versionCode.value).toBe(version.versionCode.value);
    });
  });

  describe('findById', () => {
    it('should find version by id', async () => {
      const version = createTestVersion();
      await repository.save(version);

      const found = await repository.findById(version.id);

      expect(found).not.toBeNull();
      expect(found!.id).toBe(version.id);
    });

    it('should return null for non-existent id', async () => {
      const found = await repository.findById('non-existent-id');

      expect(found).toBeNull();
    });
  });

  describe('findLatestByPlatform', () => {
    it('should find latest enabled version by platform', async () => {
      const v1 = createTestVersion();
      const v2 = AppVersion.create({
        platform: Platform.ANDROID,
        versionCode: VersionCode.create(200),
        versionName: VersionName.create('2.0.0'),
        buildNumber: BuildNumber.create('200'),
        downloadUrl: DownloadUrl.create('https://example.com/app-v2.apk'),
        fileSize: FileSize.create(20971520n),
        fileSha256: FileSha256.create('0000000000000000000000000000000000000000000000000000000000000000'),
        changelog: Changelog.create('Version 2.0.0 with new features'),
        isForceUpdate: false,
        createdBy: 'test-user',
      });

      await repository.save(v1);
      await repository.save(v2);

      const latest = await repository.findLatestByPlatform(Platform.ANDROID);

      expect(latest).not.toBeNull();
      expect(latest!.versionCode.value).toBe(200);
    });

    it('should return null if no enabled versions exist', async () => {
      const latest = await repository.findLatestByPlatform(Platform.ANDROID);

      expect(latest).toBeNull();
    });

    it('should not return disabled versions', async () => {
      const version = createTestVersion();
      version.disable('test-user');
      await repository.save(version);

      const latest = await repository.findLatestByPlatform(Platform.ANDROID);

      expect(latest).toBeNull();
    });
  });

  describe('findByPlatformAndVersionCode', () => {
    it('should find version by platform and version code', async () => {
      const version = createTestVersion();
      await repository.save(version);

      const found = await repository.findByPlatformAndVersionCode(
        Platform.ANDROID,
        VersionCode.create(100),
      );

      expect(found).not.toBeNull();
      expect(found!.versionCode.value).toBe(100);
    });

    it('should return null if version not found', async () => {
      const found = await repository.findByPlatformAndVersionCode(
        Platform.ANDROID,
        VersionCode.create(999),
      );

      expect(found).toBeNull();
    });
  });

  describe('findAllByPlatform', () => {
    it('should find all enabled versions by default', async () => {
      const v1 = createTestVersion();
      const v2 = AppVersion.create({
        platform: Platform.ANDROID,
        versionCode: VersionCode.create(200),
        versionName: VersionName.create('2.0.0'),
        buildNumber: BuildNumber.create('200'),
        downloadUrl: DownloadUrl.create('https://example.com/app-v2.apk'),
        fileSize: FileSize.create(20971520n),
        fileSha256: FileSha256.create('0000000000000000000000000000000000000000000000000000000000000000'),
        changelog: Changelog.create('Version 2.0.0'),
        isForceUpdate: false,
        createdBy: 'test-user',
      });
      v2.disable('test-user');

      await repository.save(v1);
      await repository.save(v2);

      const versions = await repository.findAllByPlatform(Platform.ANDROID);

      expect(versions).toHaveLength(1);
      expect(versions[0].versionCode.value).toBe(100);
    });

    it('should find all versions including disabled when requested', async () => {
      const v1 = createTestVersion();
      const v2 = AppVersion.create({
        platform: Platform.ANDROID,
        versionCode: VersionCode.create(200),
        versionName: VersionName.create('2.0.0'),
        buildNumber: BuildNumber.create('200'),
        downloadUrl: DownloadUrl.create('https://example.com/app-v2.apk'),
        fileSize: FileSize.create(20971520n),
        fileSha256: FileSha256.create('0000000000000000000000000000000000000000000000000000000000000000'),
        changelog: Changelog.create('Version 2.0.0'),
        isForceUpdate: false,
        createdBy: 'test-user',
      });
      v2.disable('test-user');

      await repository.save(v1);
      await repository.save(v2);

      const versions = await repository.findAllByPlatform(Platform.ANDROID, true);

      expect(versions).toHaveLength(2);
    });
  });

  describe('update', () => {
    it('should update existing version', async () => {
      const version = createTestVersion();
      await repository.save(version);

      version.setForceUpdate(true, 'test-user');
      await repository.update(version.id, version);

      const updated = await repository.findById(version.id);

      expect(updated).not.toBeNull();
      expect(updated!.isForceUpdate).toBe(true);
    });
  });

  describe('toggleEnabled', () => {
    it('should toggle enabled status', async () => {
      const version = createTestVersion();
      await repository.save(version);

      await repository.toggleEnabled(version.id, false);

      const disabled = await repository.findById(version.id);
      expect(disabled!.isEnabled).toBe(false);

      await repository.toggleEnabled(version.id, true);

      const enabled = await repository.findById(version.id);
      expect(enabled!.isEnabled).toBe(true);
    });
  });

  describe('delete', () => {
    it('should delete version', async () => {
      const version = createTestVersion();
      await repository.save(version);

      await repository.delete(version.id);

      const found = await repository.findById(version.id);
      expect(found).toBeNull();
    });

    it('should not throw when deleting non-existent version', async () => {
      await expect(repository.delete('non-existent-id')).resolves.not.toThrow();
    });
  });
});
