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

describe('OpenFinance Excluded Transactions Module (e2e)', () => {
  let app: INestApplication;
  const API_KEY = 'JOqyUcP45H875xmuI2gT3H9dDK42I2Wt';

  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('POST /openfinance-excluded-transactions', () => {
    it('deve criar uma nova transação excluída do OpenFinance', () => {
      const excludedTransaction = {
        client_id: 'client_123',
        transaction_openfinance_id: 'txn_openfinance_456',
        transaction_date: new Date().toISOString(),
      };

      return request(app.getHttpServer())
        .post('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .send(excludedTransaction)
        .expect(201)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.client_id).toBe(excludedTransaction.client_id);
          expect(res.body.transaction_openfinance_id).toBe(
            excludedTransaction.transaction_openfinance_id,
          );
          expect(res.body.transaction_date).toBeDefined();
          expect(res.body._id).toBeDefined();
        });
    });

    it('deve retornar erro 400 para dados inválidos', () => {
      const invalidTransaction = {
        // client_id ausente - deve causar erro 400
        transaction_openfinance_id: 'txn_openfinance_456',
        transaction_date: new Date().toISOString(),
      };

      return request(app.getHttpServer())
        .post('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .send(invalidTransaction)
        .expect(400);
    });

    it('deve retornar erro 401 sem API key', () => {
      const excludedTransaction = {
        client_id: 'client_123',
        transaction_openfinance_id: 'txn_openfinance_456',
        reason: 'Transação duplicada',
      };

      return request(app.getHttpServer())
        .post('/openfinance-excluded-transactions')
        .send(excludedTransaction)
        .expect(401);
    });

    it('deve criar transação excluída com dados mínimos', () => {
      const minimalTransaction = {
        client_id: 'client_456',
        transaction_openfinance_id: 'txn_openfinance_789',
        transaction_date: new Date().toISOString(),
      };

      return request(app.getHttpServer())
        .post('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .send(minimalTransaction)
        .expect(201)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(res.body.client_id).toBe(minimalTransaction.client_id);
          expect(res.body.transaction_openfinance_id).toBe(
            minimalTransaction.transaction_openfinance_id,
          );
          expect(res.body.transaction_date).toBeDefined();
          expect(res.body._id).toBeDefined();
        });
    });
  });

  describe('GET /openfinance-excluded-transactions', () => {
    it('deve listar transações excluídas por client_id', () => {
      return request(app.getHttpServer())
        .get('/openfinance-excluded-transactions?client_id=client_123')
        .set('x-api-key', API_KEY)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(Array.isArray(res.body)).toBe(true);
        });
    });

    it('deve retornar array vazio para client_id inexistente', () => {
      return request(app.getHttpServer())
        .get('/openfinance-excluded-transactions?client_id=client_inexistente')
        .set('x-api-key', API_KEY)
        .expect(200)
        .expect((res) => {
          expect(res.body).toBeDefined();
          expect(Array.isArray(res.body)).toBe(true);
          expect(res.body.length).toBe(0);
        });
    });

    it('deve retornar erro 400 quando client_id não é fornecido', () => {
      return request(app.getHttpServer())
        .get('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .expect(400);
    });

    it('deve retornar erro 401 sem API key', () => {
      return request(app.getHttpServer())
        .get('/openfinance-excluded-transactions?client_id=client_123')
        .expect(401);
    });

    it('deve listar múltiplas transações excluídas', () => {
      // Primeiro, criar algumas transações excluídas
      const transactions = [
        {
          client_id: 'client_multi_1',
          transaction_openfinance_id: 'txn_multi_1',
          transaction_date: new Date().toISOString(),
        },
        {
          client_id: 'client_multi_1',
          transaction_openfinance_id: 'txn_multi_2',
          transaction_date: new Date().toISOString(),
        },
      ];

      // Criar as transações
      return Promise.all(
        transactions.map((txn) =>
          request(app.getHttpServer())
            .post('/openfinance-excluded-transactions')
            .set('x-api-key', API_KEY)
            .send(txn),
        ),
      ).then(() => {
        // Buscar todas as transações do client
        return request(app.getHttpServer())
          .get('/openfinance-excluded-transactions?client_id=client_multi_1')
          .set('x-api-key', API_KEY)
          .expect(200)
          .expect((res) => {
            expect(res.body).toBeDefined();
            expect(Array.isArray(res.body)).toBe(true);
            expect(res.body.length).toBeGreaterThanOrEqual(2);
          });
      });
    });
  });

  describe('DELETE /openfinance-excluded-transactions', () => {
    it('deve remover uma transação excluída', () => {
      // Primeiro, criar uma transação para depois remover
      const transactionToDelete = {
        client_id: 'client_delete_test',
        transaction_openfinance_id: 'txn_to_delete_123',
        transaction_date: new Date().toISOString(),
      };

      return request(app.getHttpServer())
        .post('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .send(transactionToDelete)
        .expect(201)
        .then((createRes) => {
          // Agora remover a transação
          return request(app.getHttpServer())
            .delete(
              `/openfinance-excluded-transactions?transaction_openfinance_id=${transactionToDelete.transaction_openfinance_id}`,
            )
            .set('x-api-key', API_KEY)
            .expect(200)
            .expect((deleteRes) => {
              expect(deleteRes.body).toBeDefined();
              expect(deleteRes.body.message).toContain('removida');
            });
        });
    });

    it('deve retornar erro 404 para transação inexistente', () => {
      return request(app.getHttpServer())
        .delete(
          '/openfinance-excluded-transactions?transaction_openfinance_id=txn_inexistente',
        )
        .set('x-api-key', API_KEY)
        .expect(404);
    });

    it('deve retornar erro 400 quando transaction_openfinance_id não é fornecido', () => {
      return request(app.getHttpServer())
        .delete('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .expect(400);
    });

    it('deve retornar erro 401 sem API key', () => {
      return request(app.getHttpServer())
        .delete(
          '/openfinance-excluded-transactions?transaction_openfinance_id=txn_123',
        )
        .expect(401);
    });
  });

  describe('Fluxo completo de transações excluídas', () => {
    it('deve executar fluxo completo: criar, listar e remover', () => {
      const testTransaction = {
        client_id: 'client_fluxo_completo',
        transaction_openfinance_id: 'txn_fluxo_completo_123',
        transaction_date: new Date().toISOString(),
      };

      // 1. Criar transação
      return request(app.getHttpServer())
        .post('/openfinance-excluded-transactions')
        .set('x-api-key', API_KEY)
        .send(testTransaction)
        .expect(201)
        .then((createRes) => {
          expect(createRes.body.transaction_openfinance_id).toBe(
            testTransaction.transaction_openfinance_id,
          );

          // 2. Listar transações do client
          return request(app.getHttpServer())
            .get(
              `/openfinance-excluded-transactions?client_id=${testTransaction.client_id}`,
            )
            .set('x-api-key', API_KEY)
            .expect(200);
        })
        .then((listRes) => {
          expect(listRes.body).toBeDefined();
          expect(Array.isArray(listRes.body)).toBe(true);
          expect(listRes.body.length).toBeGreaterThan(0);

          // 3. Remover transação
          return request(app.getHttpServer())
            .delete(
              `/openfinance-excluded-transactions?transaction_openfinance_id=${testTransaction.transaction_openfinance_id}`,
            )
            .set('x-api-key', API_KEY)
            .expect(200);
        })
        .then((deleteRes) => {
          expect(deleteRes.body).toBeDefined();
          expect(deleteRes.body.message).toContain('removida');

          // 4. Verificar que a transação foi removida
          return request(app.getHttpServer())
            .get(
              `/openfinance-excluded-transactions?client_id=${testTransaction.client_id}`,
            )
            .set('x-api-key', API_KEY)
            .expect(200);
        })
        .then((finalListRes) => {
          expect(finalListRes.body).toBeDefined();
          expect(Array.isArray(finalListRes.body)).toBe(true);
          // A transação deve ter sido removida
          const remainingTransactions = finalListRes.body.filter(
            (txn) =>
              txn.transaction_openfinance_id ===
              testTransaction.transaction_openfinance_id,
          );
          expect(remainingTransactions.length).toBe(0);
        });
    });
  });
});
