import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import * as jwt from 'jsonwebtoken';

describe('Laravel API Module (e2e)', () => {
  let app: INestApplication;
  const API_KEY = 'JOqyUcP45H875xmuI2gT3H9dDK42I2Wt';

  // Tokens de teste
  const validUserToken = jwt.sign(
    {
      sub: 'user_123',
      iss: 'planfi-bff',
      jti: 'jwt_id_123',
      prv: 'user',
      exp: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hora
      iat: Math.floor(Date.now() / 1000),
      nbf: Math.floor(Date.now() / 1000),
      uuid: 'user-uuid-123',
      email: 'user@test.com',
      name: 'Usuário Teste',
    },
    'test-secret-key',
  );

  const validClientToken = jwt.sign(
    {
      sub: 'client_456',
      iss: 'planfi-bff',
      jti: 'jwt_id_456',
      prv: 'client',
      exp: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hora
      iat: Math.floor(Date.now() / 1000),
      nbf: Math.floor(Date.now() / 1000),
      uuid: 'client-uuid-456',
      email: 'client@test.com',
      name: 'Client Teste',
    },
    'test-secret-key',
  );

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();

    // Adicionar ValidationPipe para validação de DTOs
    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: false, // Permitir propriedades adicionais para DTOs dinâmicos
        transform: true,
      }),
    );

    await app.init();
  });

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

  describe('GET /laravel-api/health', () => {
    it('deve verificar saúde da integração com Laravel', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/health')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.message).toContain(
            'Integração BFF -> Laravel API funcionando',
          );
          expect(res.body.user).toBeDefined();
          expect(res.body.user.id).toBe('user_123');
          expect(res.body.user.type).toBe('user');
          expect(res.body.tokenAvailable).toBe(true);
        });
    });

    it('deve retornar erro 401 sem API key', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/health')
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(401);
    });

    it('deve retornar erro 401 sem token JWT', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/health')
        .set('x-api-key', API_KEY)
        .expect(401);
    });
  });

  describe('POST /laravel-api/validate-token', () => {
    it('deve validar token com API Laravel', () => {
      return request(app.getHttpServer())
        .post('/laravel-api/validate-token')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.bffUser).toBeDefined();
          expect(res.body.bffUser.id).toBe('user_123');
          expect(res.body.bffUser.type).toBe('user');
        });
    });

    it('deve retornar erro 401 sem token JWT', () => {
      return request(app.getHttpServer())
        .post('/laravel-api/validate-token')
        .set('x-api-key', API_KEY)
        .expect(401);
    });
  });

  describe('GET /laravel-api/user/profile', () => {
    it('deve obter perfil do usuário da API Laravel', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/user/profile')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.source).toBe('Laravel API');
          expect(res.body.tokenData).toBeDefined();
          expect(res.body.tokenData.uuid).toBe('user-uuid-123');
          expect(res.body.tokenData.email).toBe('user@test.com');
          expect(res.body.tokenData.name).toBe('Usuário Teste');
          expect(res.body.laravelData).toBeDefined();
        });
    });

    it('deve retornar erro 403 com token de client', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/user/profile')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validClientToken}`)
        .expect(403);
    });

    it('deve retornar erro 401 sem token JWT', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/user/profile')
        .set('x-api-key', API_KEY)
        .expect(401);
    });
  });

  describe('GET /laravel-api/client/profile', () => {
    it('deve obter perfil do client da API Laravel', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/client/profile')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validClientToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.source).toBe('Laravel API');
          expect(res.body.tokenData).toBeDefined();
          expect(res.body.laravelData).toBeDefined();
        });
    });

    it('deve retornar erro 403 com token de usuário', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/client/profile')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(403);
    });

    it('deve retornar erro 401 sem token JWT', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/client/profile')
        .set('x-api-key', API_KEY)
        .expect(401);
    });
  });

  describe('POST /laravel-api/proxy/*path', () => {
    it('deve fazer proxy de requisição GET para Laravel', () => {
      const proxyRequest = {
        method: 'GET',
        params: { id: '123' },
      };

      return request(app.getHttpServer())
        .post('/laravel-api/proxy/test-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .send(proxyRequest)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.source).toBe('Laravel API via BFF Proxy');
          expect(res.body.endpoint).toBe('/test-endpoint');
          expect(res.body.method).toBe('GET');
          expect(res.body.bffUser).toBeDefined();
          expect(res.body.bffUser.id).toBe('user_123');
          expect(res.body.bffUser.type).toBe('user');
          expect(res.body.response).toBeDefined();
        });
    });

    it('deve fazer proxy de requisição POST para Laravel', () => {
      const proxyRequest = {
        method: 'POST',
        data: { name: 'Teste', value: 123 },
      };

      return request(app.getHttpServer())
        .post('/laravel-api/proxy/test-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .send(proxyRequest)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.source).toBe('Laravel API via BFF Proxy');
          expect(res.body.endpoint).toBe('/test-endpoint');
          expect(res.body.method).toBe('POST');
          expect(res.body.bffUser).toBeDefined();
          expect(res.body.response).toBeDefined();
        });
    });

    it('deve usar GET como método padrão quando não especificado', () => {
      const proxyRequest = {
        params: { id: '123' },
      };

      return request(app.getHttpServer())
        .post('/laravel-api/proxy/test-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .send(proxyRequest)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.method).toBe('GET');
        });
    });

    it('deve retornar erro 401 sem token JWT', () => {
      const proxyRequest = {
        method: 'GET',
      };

      return request(app.getHttpServer())
        .post('/laravel-api/proxy/test-endpoint')
        .set('x-api-key', API_KEY)
        .send(proxyRequest)
        .expect(401);
    });
  });

  describe('GET /laravel-api/debug/token', () => {
    it('deve retornar debug completo do token', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/debug/token')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.message).toBe('Debug completo do token JWT');
          expect(res.body.data).toBeDefined();
          expect(res.body.data.user).toBeDefined();
          expect(res.body.data.user.id).toBe('user_123');
          expect(res.body.data.user.type).toBe('user');
          expect(res.body.data.rawToken).toBeDefined();
          expect(res.body.data.tokenData).toBeDefined();
          expect(res.body.data.jwtPayload).toBeDefined();
          expect(res.body.data.extractedFields).toBeDefined();
          expect(res.body.data.extractedFields.uuid).toBe('user-uuid-123');
          expect(res.body.data.extractedFields.email).toBe('user@test.com');
          expect(res.body.data.extractedFields.name).toBe('Usuário Teste');
        });
    });
  });

  describe('GET /laravel-api/test/user-endpoint', () => {
    it('deve testar endpoint de usuário da API Laravel', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/test/user-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.message).toBe(
            'Teste de endpoint de usuário da API Laravel',
          );
          expect(res.body.response).toBeDefined();
          expect(res.body.status).toBeDefined();
        });
    });

    it('deve retornar erro 403 com token de client', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/test/user-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validClientToken}`)
        .expect(403);
    });
  });

  describe('GET /laravel-api/test/client-endpoint', () => {
    it('deve testar endpoint de client da API Laravel', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/test/client-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validClientToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.message).toBe(
            'Teste de endpoint de client da API Laravel',
          );
          expect(res.body.response).toBeDefined();
          expect(res.body.status).toBeDefined();
        });
    });

    it('deve retornar erro 403 com token de usuário', () => {
      return request(app.getHttpServer())
        .get('/laravel-api/test/client-endpoint')
        .set('x-api-key', API_KEY)
        .set('Authorization', `Bearer ${validUserToken}`)
        .expect(403);
    });
  });
});
