import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe, ExecutionContext } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../../src/app.module';
import { GlobalExceptionFilter } from '../../src/shared/filters/global-exception.filter';
import { PrismaService } from '../../src/infrastructure/persistence/prisma/prisma.service';
import { JwtAuthGuard } from '../../src/shared/guards/jwt-auth.guard';

describe('Analytics API (E2E)', () => {
  let app: INestApplication;
  let prisma: PrismaService;

  const mockJwtToken = 'test-jwt-token';

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    })
      .overrideGuard(JwtAuthGuard)
      .useValue({
        canActivate: (context: ExecutionContext) => {
          const req = context.switchToHttp().getRequest();
          req.user = { userId: '12345' };
          return true;
        },
      })
      .compile();

    app = moduleFixture.createNestApplication();

    app.useGlobalFilters(new GlobalExceptionFilter());
    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        transform: true,
        forbidNonWhitelisted: true,
      }),
    );
    app.setGlobalPrefix('api/v1');

    await app.init();

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

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

  describe('POST /api/v1/analytics/events', () => {
    it('should accept batch events', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/analytics/events')
        .send({
          events: [
            {
              installId: 'test-install-id-12345',
              eventName: 'app_session_start',
              clientTs: Math.floor(Date.now() / 1000),
              properties: {
                os: 'iOS',
                osVersion: '17.0',
                appVersion: '1.0.0',
              },
            },
          ],
        })
        .expect(201);

      expect(response.body).toHaveProperty('accepted');
      expect(response.body.accepted).toBeGreaterThanOrEqual(0);
    });

    it('should accept multiple events in batch', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/analytics/events')
        .send({
          events: [
            {
              installId: 'test-install-id-12345',
              eventName: 'app_session_start',
              clientTs: Math.floor(Date.now() / 1000),
              properties: { os: 'iOS' },
            },
            {
              installId: 'test-install-id-12345',
              eventName: 'presence_heartbeat',
              clientTs: Math.floor(Date.now() / 1000),
              properties: { os: 'iOS' },
            },
            {
              installId: 'test-install-id-12345',
              eventName: 'app_session_end',
              clientTs: Math.floor(Date.now() / 1000),
              properties: { os: 'iOS' },
            },
          ],
        })
        .expect(201);

      expect(response.body.accepted).toBeGreaterThanOrEqual(0);
    });

    it('should validate event name format', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/analytics/events')
        .send({
          events: [
            {
              installId: 'test-install-id-12345',
              eventName: '123_invalid', // Invalid: starts with number
              clientTs: Math.floor(Date.now() / 1000),
            },
          ],
        });

      // May return 201 with failed count or 400 depending on implementation
      // Check that validation occurs
    });

    it('should validate installId format', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/analytics/events')
        .send({
          events: [
            {
              installId: 'short', // Invalid: too short
              eventName: 'app_session_start',
              clientTs: Math.floor(Date.now() / 1000),
            },
          ],
        });

      // May return 201 with failed count or 400 depending on implementation
    });

    it('should handle empty events array', async () => {
      const response = await request(app.getHttpServer())
        .post('/api/v1/analytics/events')
        .send({
          events: [],
        })
        .expect(201);

      expect(response.body.accepted).toBe(0);
    });
  });

  describe('GET /api/v1/analytics/dau', () => {
    it('should return DAU statistics', async () => {
      const startDate = '2025-01-01';
      const endDate = '2025-01-15';

      const response = await request(app.getHttpServer())
        .get('/api/v1/analytics/dau')
        .query({ startDate, endDate })
        .set('Authorization', `Bearer ${mockJwtToken}`)
        .expect(200);

      expect(response.body).toHaveProperty('data');
      expect(Array.isArray(response.body.data)).toBe(true);
      expect(response.body).toHaveProperty('total');
    });

    it('should validate date format', async () => {
      const response = await request(app.getHttpServer())
        .get('/api/v1/analytics/dau')
        .query({
          startDate: 'invalid-date',
          endDate: '2025-01-15',
        })
        .set('Authorization', `Bearer ${mockJwtToken}`)
        .expect(400);

      expect(response.body.statusCode).toBe(400);
    });

    it('should require startDate parameter', async () => {
      const response = await request(app.getHttpServer())
        .get('/api/v1/analytics/dau')
        .query({ endDate: '2025-01-15' })
        .set('Authorization', `Bearer ${mockJwtToken}`)
        .expect(400);

      expect(response.body.statusCode).toBe(400);
    });

    it('should require endDate parameter', async () => {
      const response = await request(app.getHttpServer())
        .get('/api/v1/analytics/dau')
        .query({ startDate: '2025-01-01' })
        .set('Authorization', `Bearer ${mockJwtToken}`)
        .expect(400);

      expect(response.body.statusCode).toBe(400);
    });
  });
});
