import { Test, TestingModule } from '@nestjs/testing';
import {
  INestApplication,
  BadRequestException,
  UnauthorizedException,
} from '@nestjs/common';
import * as request from 'supertest';
import { PluggyController } from '../src/pluggy/pluggy.controller';
import { PluggyWebhookController } from '../src/pluggy-webhook/pluggy-webhook.controller';
import { PluggyService } from '../src/pluggy/pluggy.service';
import { PluggyWebhookService } from '../src/pluggy-webhook/pluggy-webhook.service';
import { ApiKeyGuard } from '../src/common/guards/api-key/api-key.guard';
import { ConfigModule } from '@nestjs/config';

// Mock do service do Pluggy
const mockPluggyService = {
  makeRequest: jest.fn(),
  getConnectToken: jest.fn(),
};

// Mock do service do Pluggy Webhook
const mockPluggyWebhookService = {
  processWebhook: jest.fn(),
};

// Mock do API Key Guard
const mockApiKeyGuard = {
  canActivate: jest.fn(() => true),
};

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

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({
          isGlobal: true,
          ignoreEnvFile: true,
          load: [
            () => ({
              API_KEY: 'JOqyUcP45H875xmuI2gT3H9dDK42I2Wt',
              PLUGGY_CLIENT_ID: 'test-client-id',
              PLUGGY_CLIENT_SECRET: 'test-client-secret',
              PLUGGY_API_URL: 'https://api.pluggy.ai',
            }),
          ],
        }),
      ],
      controllers: [PluggyController, PluggyWebhookController],
      providers: [
        {
          provide: PluggyService,
          useValue: mockPluggyService,
        },
        {
          provide: PluggyWebhookService,
          useValue: mockPluggyWebhookService,
        },
        {
          provide: ApiKeyGuard,
          useValue: mockApiKeyGuard,
        },
      ],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

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

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('POST /pluggy', () => {
    it('deve processar requisição para API Pluggy', () => {
      const pluggyRequest = {
        method: 'GET',
        endpoint: '/items',
        data: {},
      };

      const mockResponse = {
        results: [
          {
            id: 'item_123',
            status: 'UPDATED',
            institution: {
              id: 'institution_1',
              name: 'Banco do Brasil',
            },
          },
        ],
        page: 1,
        totalPages: 1,
        total: 1,
      };

      mockPluggyService.makeRequest.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy')
        .set('x-api-key', API_KEY)
        .send(pluggyRequest)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyService.makeRequest).toHaveBeenCalledWith(
            '/items',
            'GET',
            pluggyRequest,
          );
        });
    });

    it('deve retornar erro 400 para dados inválidos', () => {
      const invalidRequest = {
        // method ausente
        endpoint: '/items',
      };

      mockPluggyService.makeRequest.mockRejectedValue(
        new BadRequestException('Method is required'),
      );

      return request(app.getHttpServer())
        .post('/pluggy')
        .set('x-api-key', API_KEY)
        .send(invalidRequest)
        .expect(400);
    });

    it('deve retornar erro 401 sem API key', () => {
      mockApiKeyGuard.canActivate.mockReturnValue(false);

      return request(app.getHttpServer())
        .post('/pluggy')
        .send({ method: 'GET', endpoint: '/items' })
        .expect(401);
    });

    it('deve processar requisição POST para criar item', () => {
      const createItemRequest = {
        method: 'POST',
        endpoint: '/items',
        data: {
          institutionId: 'institution_1',
          parameters: {
            username: 'user@example.com',
            password: 'password123',
          },
        },
      };

      const mockResponse = {
        id: 'item_new',
        status: 'CREATED',
        institution: {
          id: 'institution_1',
          name: 'Banco do Brasil',
        },
        createdAt: '2024-01-15T10:00:00Z',
      };

      mockPluggyService.makeRequest.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy')
        .set('x-api-key', API_KEY)
        .send(createItemRequest)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyService.makeRequest).toHaveBeenCalledWith(
            '/items',
            'POST',
            createItemRequest,
          );
        });
    });
  });

  describe('POST /pluggy/connect_token', () => {
    it('deve obter connect token para item específico', () => {
      const connectTokenRequest = {
        itemId: 'item_123',
      };

      const mockResponse = {
        connectToken: 'connect_token_abc123',
        expiresIn: 3600,
        itemId: 'item_123',
      };

      mockPluggyService.getConnectToken.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy/connect_token')
        .set('x-api-key', API_KEY)
        .send(connectTokenRequest)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyService.getConnectToken).toHaveBeenCalledWith(
            'item_123',
          );
        });
    });

    it('deve retornar erro 400 para itemId ausente', () => {
      const invalidRequest = {
        // itemId ausente
      };

      mockPluggyService.getConnectToken.mockRejectedValue(
        new BadRequestException('itemId is required'),
      );

      return request(app.getHttpServer())
        .post('/pluggy/connect_token')
        .set('x-api-key', API_KEY)
        .send(invalidRequest)
        .expect(400);
    });

    it('deve retornar erro 401 sem API key', () => {
      mockApiKeyGuard.canActivate.mockReturnValue(false);

      return request(app.getHttpServer())
        .post('/pluggy/connect_token')
        .send({ itemId: 'item_123' })
        .expect(401);
    });
  });

  describe('POST /pluggy-webhook', () => {
    it('deve processar webhook do Pluggy', () => {
      const webhookData = {
        type: 'ITEM_UPDATED',
        itemId: 'item_123',
        status: 'UPDATED',
        timestamp: '2024-01-15T10:00:00Z',
        data: {
          accounts: [
            {
              id: 'account_1',
              name: 'Conta Corrente',
              type: 'BANK',
              balance: 1500.0,
            },
          ],
        },
      };

      const mockResponse = {
        received: true,
      };

      mockPluggyWebhookService.processWebhook.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy-webhook')
        .send(webhookData)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyWebhookService.processWebhook).toHaveBeenCalledWith(
            webhookData,
          );
        });
    });

    it('deve processar webhook de erro', () => {
      const errorWebhook = {
        type: 'ITEM_ERROR',
        itemId: 'item_123',
        error: {
          code: 'INVALID_CREDENTIALS',
          message: 'Invalid username or password',
        },
        timestamp: '2024-01-15T10:00:00Z',
      };

      const mockResponse = {
        received: true,
      };

      mockPluggyWebhookService.processWebhook.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy-webhook')
        .send(errorWebhook)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyWebhookService.processWebhook).toHaveBeenCalledWith(
            errorWebhook,
          );
        });
    });

    it('deve processar webhook de transação', () => {
      const transactionWebhook = {
        type: 'TRANSACTION_CREATED',
        itemId: 'item_123',
        accountId: 'account_1',
        transaction: {
          id: 'transaction_1',
          description: 'Transferência recebida',
          amount: 500.0,
          date: '2024-01-15',
          category: 'TRANSFER',
        },
        timestamp: '2024-01-15T10:00:00Z',
      };

      const mockResponse = {
        received: true,
      };

      mockPluggyWebhookService.processWebhook.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy-webhook')
        .send(transactionWebhook)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyWebhookService.processWebhook).toHaveBeenCalledWith(
            transactionWebhook,
          );
        });
    });

    it('deve processar webhook de conta atualizada', () => {
      const accountWebhook = {
        type: 'ACCOUNT_UPDATED',
        itemId: 'item_123',
        account: {
          id: 'account_1',
          name: 'Conta Corrente',
          type: 'BANK',
          balance: 2000.0,
          updatedAt: '2024-01-15T10:00:00Z',
        },
        timestamp: '2024-01-15T10:00:00Z',
      };

      const mockResponse = {
        received: true,
      };

      mockPluggyWebhookService.processWebhook.mockResolvedValue(mockResponse);

      return request(app.getHttpServer())
        .post('/pluggy-webhook')
        .send(accountWebhook)
        .expect(200)
        .expect((res) => {
          expect(res.body).toEqual(mockResponse);
          expect(mockPluggyWebhookService.processWebhook).toHaveBeenCalledWith(
            accountWebhook,
          );
        });
    });

    it('deve retornar erro 400 para webhook malformado', () => {
      const malformedWebhook = {
        // type ausente
        itemId: 'item_123',
      };

      mockPluggyWebhookService.processWebhook.mockRejectedValue(
        new BadRequestException('Invalid webhook format'),
      );

      return request(app.getHttpServer())
        .post('/pluggy-webhook')
        .send(malformedWebhook)
        .expect(400);
    });
  });
});
