import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as request from 'supertest';
import { PrismaService } from '../../src/infrastructure/persistence/prisma/prisma.service';
import { AppModule } from '../../src/app.module';
import { Platform } from '../../src/domain/enums/platform.enum';
import { Platform as PrismaPlatform } from '@prisma/client';

describe('VersionController (e2e)', () => {
  let app: INestApplication;
  let prisma: PrismaService;

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

    app = moduleFixture.createNestApplication();

    // Apply same middleware as main.ts
    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        transform: true,
        forbidNonWhitelisted: true,
      }),
    );

    const apiPrefix = process.env.API_PREFIX || 'api/v1';
    app.setGlobalPrefix(apiPrefix);

    await app.init();

    prisma = moduleFixture.get<PrismaService>(PrismaService);
    await prisma.$connect();
  });

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

  beforeEach(async () => {
    await prisma.appVersion.deleteMany({});
  });

  const apiPrefix = process.env.API_PREFIX || 'api/v1';

  describe('/version (POST)', () => {
    it('should create new Android version', () => {
      return request(app.getHttpServer())
        .post(`/${apiPrefix}/version`)
        .send({
          platform: 'android',
          versionCode: 100,
          versionName: '1.0.0',
          buildNumber: '100',
          downloadUrl: 'https://example.com/app.apk',
          fileSize: '10485760',
          fileSha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
          changelog: 'Initial release with basic features',
          minOsVersion: '5.0',
          isEnabled: true,
          isForceUpdate: false,
          createdBy: 'admin',
        })
        .expect(201)
        .expect((res) => {
          expect(res.body.platform).toBe('android');
          expect(res.body.versionCode).toBe(100);
          expect(res.body.versionName).toBe('1.0.0');
        });
    });

    it('should create new iOS version', () => {
      return request(app.getHttpServer())
        .post(`/${apiPrefix}/version`)
        .send({
          platform: 'ios',
          versionCode: 100,
          versionName: '1.0.0',
          buildNumber: '100',
          downloadUrl: 'https://example.com/app.ipa',
          fileSize: '15728640',
          fileSha256: '0000000000000000000000000000000000000000000000000000000000000000',
          changelog: 'iOS initial release',
          minOsVersion: '14.0',
          isEnabled: true,
          isForceUpdate: false,
          createdBy: 'admin',
        })
        .expect(201)
        .expect((res) => {
          expect(res.body.platform).toBe('ios');
          expect(res.body.versionCode).toBe(100);
        });
    });

    it('should reject invalid version code', () => {
      return request(app.getHttpServer())
        .post(`/${apiPrefix}/version`)
        .send({
          platform: 'android',
          versionCode: -1,
          versionName: '1.0.0',
          buildNumber: '100',
          downloadUrl: 'https://example.com/app.apk',
          fileSize: '10485760',
          fileSha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
          changelog: 'Invalid version',
          minOsVersion: '5.0',
          isEnabled: true,
          isForceUpdate: false,
          createdBy: 'admin',
        })
        .expect(400);
    });

    it('should reject invalid version name format', () => {
      return request(app.getHttpServer())
        .post(`/${apiPrefix}/version`)
        .send({
          platform: 'android',
          versionCode: 100,
          versionName: '1.0',
          buildNumber: '100',
          downloadUrl: 'https://example.com/app.apk',
          fileSize: '10485760',
          fileSha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
          changelog: 'Invalid version name',
          minOsVersion: '5.0',
          isEnabled: true,
          isForceUpdate: false,
          createdBy: 'admin',
        })
        .expect(400);
    });

    it('should reject invalid download URL', () => {
      return request(app.getHttpServer())
        .post(`/${apiPrefix}/version`)
        .send({
          platform: 'android',
          versionCode: 100,
          versionName: '1.0.0',
          buildNumber: '100',
          downloadUrl: 'not-a-url',
          fileSize: '10485760',
          fileSha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
          changelog: 'Invalid URL',
          minOsVersion: '5.0',
          isEnabled: true,
          isForceUpdate: false,
          createdBy: 'admin',
        })
        .expect(400);
    });

    it('should reject invalid SHA256', () => {
      return request(app.getHttpServer())
        .post(`/${apiPrefix}/version`)
        .send({
          platform: 'android',
          versionCode: 100,
          versionName: '1.0.0',
          buildNumber: '100',
          downloadUrl: 'https://example.com/app.apk',
          fileSize: '10485760',
          fileSha256: 'invalid-sha256',
          changelog: 'Invalid SHA256',
          minOsVersion: '5.0',
          isEnabled: true,
          isForceUpdate: false,
          createdBy: 'admin',
        })
        .expect(400);
    });
  });

  describe('/version/check-update (GET)', () => {
    beforeEach(async () => {
      // Create test versions
      await prisma.appVersion.create({
        data: {
          id: 'test-v1',
          platform: PrismaPlatform.ANDROID,
          versionCode: 100,
          versionName: '1.0.0',
          buildNumber: '100',
          downloadUrl: 'https://example.com/app-v1.apk',
          fileSize: 10485760n,
          fileSha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
          changelog: 'Version 1.0.0',
          minOsVersion: '5.0',
          isEnabled: true,
          isForceUpdate: false,
          releaseDate: null,
          createdBy: 'admin',
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      });

      await prisma.appVersion.create({
        data: {
          id: 'test-v2',
          platform: PrismaPlatform.ANDROID,
          versionCode: 200,
          versionName: '2.0.0',
          buildNumber: '200',
          downloadUrl: 'https://example.com/app-v2.apk',
          fileSize: 20971520n,
          fileSha256: '0000000000000000000000000000000000000000000000000000000000000000',
          changelog: 'Version 2.0.0',
          minOsVersion: '6.0',
          isEnabled: true,
          isForceUpdate: false,
          releaseDate: null,
          createdBy: 'admin',
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      });
    });

    it('should return update available for older version', () => {
      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/check-update`)
        .query({
          platform: 'android',
          currentVersionCode: 100,
        })
        .expect(200)
        .expect((res) => {
          expect(res.body.hasUpdate).toBe(true);
          expect(res.body.latestVersion).toBeDefined();
          expect(res.body.latestVersion.versionCode).toBe(200);
          expect(res.body.isForceUpdate).toBe(false);
        });
    });

    it('should return no update for latest version', () => {
      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/check-update`)
        .query({
          platform: 'android',
          currentVersionCode: 200,
        })
        .expect(200)
        .expect((res) => {
          expect(res.body.hasUpdate).toBe(false);
          expect(res.body.latestVersion).toBeNull();
        });
    });

    it('should return force update flag when enabled', async () => {
      // Update version to force update
      await prisma.appVersion.update({
        where: { id: 'test-v2' },
        data: { isForceUpdate: true },
      });

      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/check-update`)
        .query({
          platform: 'android',
          currentVersionCode: 100,
        })
        .expect(200)
        .expect((res) => {
          expect(res.body.hasUpdate).toBe(true);
          expect(res.body.isForceUpdate).toBe(true);
        });
    });

    it('should reject invalid platform', () => {
      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/check-update`)
        .query({
          platform: 'invalid',
          currentVersionCode: 100,
        })
        .expect(400);
    });

    it('should reject invalid version code', () => {
      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/check-update`)
        .query({
          platform: 'android',
          currentVersionCode: -1,
        })
        .expect(400);
    });
  });

  describe('/version/:platform/latest (GET)', () => {
    beforeEach(async () => {
      await prisma.appVersion.create({
        data: {
          id: 'test-latest',
          platform: PrismaPlatform.ANDROID,
          versionCode: 300,
          versionName: '3.0.0',
          buildNumber: '300',
          downloadUrl: 'https://example.com/app-v3.apk',
          fileSize: 31457280n,
          fileSha256: '1111111111111111111111111111111111111111111111111111111111111111',
          changelog: 'Latest version',
          minOsVersion: '7.0',
          isEnabled: true,
          isForceUpdate: false,
          releaseDate: null,
          createdBy: 'admin',
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      });
    });

    it('should get latest version for platform', () => {
      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/android/latest`)
        .expect(200)
        .expect((res) => {
          expect(res.body.platform).toBe('android');
          expect(res.body.versionCode).toBe(300);
          expect(res.body.versionName).toBe('3.0.0');
        });
    });

    it('should return 404 for platform with no versions', () => {
      return request(app.getHttpServer())
        .get(`/${apiPrefix}/version/ios/latest`)
        .expect(404);
    });
  });
});
