import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { PluggyService } from './pluggy.service';

describe('PluggyService', () => {
  let service: PluggyService;
  let configService: ConfigService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        PluggyService,
        {
          provide: ConfigService,
          useValue: {
            get: jest.fn().mockReturnValue('https://api.pluggy.ai'),
          },
        },
      ],
    }).compile();

    service = module.get<PluggyService>(PluggyService);
    configService = module.get<ConfigService>(ConfigService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('makeRequest', () => {
    it('should return mock data in test environment', async () => {
      const result = await service.makeRequest('test-endpoint', 'GET', {
        test: 'param',
      });

      expect(result).toEqual({
        success: true,
        data: {
          message: 'Mock response for request',
          endpoint: 'test-endpoint',
          method: 'GET',
          params: { test: 'param' },
        },
      });
    });

    it('should handle POST request with data', async () => {
      const result = await service.makeRequest('test-endpoint', 'POST', {
        name: 'Test Item',
        type: 'bank',
      });

      expect(result).toEqual({
        success: true,
        data: {
          message: 'Mock response for request',
          endpoint: 'test-endpoint',
          method: 'POST',
          params: { name: 'Test Item', type: 'bank' },
        },
      });
    });

    it('should handle PUT request', async () => {
      const result = await service.makeRequest('test-endpoint', 'PUT', {
        id: '123',
        status: 'active',
      });

      expect(result).toEqual({
        success: true,
        data: {
          message: 'Mock response for request',
          endpoint: 'test-endpoint',
          method: 'PUT',
          params: { id: '123', status: 'active' },
        },
      });
    });

    it('should handle DELETE request', async () => {
      const result = await service.makeRequest('test-endpoint', 'DELETE', {
        id: '123',
      });

      expect(result).toEqual({
        success: true,
        data: {
          message: 'Mock response for request',
          endpoint: 'test-endpoint',
          method: 'DELETE',
          params: { id: '123' },
        },
      });
    });

    it('should handle PATCH request', async () => {
      const result = await service.makeRequest('test-endpoint', 'PATCH', {
        id: '123',
        updates: { status: 'updated' },
      });

      expect(result).toEqual({
        success: true,
        data: {
          message: 'Mock response for request',
          endpoint: 'test-endpoint',
          method: 'PATCH',
          params: { id: '123', updates: { status: 'updated' } },
        },
      });
    });
  });

  describe('getConnectToken', () => {
    it('should return mock connect token in test environment', async () => {
      const itemId = 'test-item-123';

      const result = await service.getConnectToken(itemId);

      expect(result).toEqual({
        accessToken: 'mock_connect_token_123',
        itemId: itemId,
      });
    });

    it('should throw error when itemId is not provided', async () => {
      await expect(service.getConnectToken()).rejects.toThrow(
        'itemId is required for connect token',
      );
    });

    it('should throw error when itemId is empty string', async () => {
      await expect(service.getConnectToken('')).rejects.toThrow(
        'itemId is required for connect token',
      );
    });

    it('should throw error when itemId is null', async () => {
      await expect(service.getConnectToken(null as any)).rejects.toThrow(
        'itemId is required for connect token',
      );
    });

    it('should throw error when itemId is undefined', async () => {
      await expect(service.getConnectToken(undefined as any)).rejects.toThrow(
        'itemId is required for connect token',
      );
    });
  });

  describe('configuration', () => {
    it('should use default URL when PLUGGY_API_URL is not set', () => {
      expect(configService.get).toHaveBeenCalledWith('PLUGGY_API_URL');
    });

    it('should use custom URL when PLUGGY_API_URL is set', async () => {
      const customUrl = 'https://custom.pluggy.ai';
      const customConfigService = {
        get: jest.fn().mockReturnValue(customUrl),
      };

      const module: TestingModule = await Test.createTestingModule({
        providers: [
          PluggyService,
          {
            provide: ConfigService,
            useValue: customConfigService,
          },
        ],
      }).compile();

      const customService = module.get<PluggyService>(PluggyService);
      expect(customService).toBeDefined();
      expect(customConfigService.get).toHaveBeenCalledWith('PLUGGY_API_URL');
    });
  });

  describe('private methods', () => {
    it('should handle getApiKey in test environment', async () => {
      // Since getApiKey is private, we test it indirectly through makeRequest
      const result = await service.makeRequest('test', 'GET', {});

      // The mock response should be returned, indicating the private method works
      expect(result).toHaveProperty('success', true);
    });
  });

  describe('error handling', () => {
    it('should handle errors gracefully in makeRequest', async () => {
      // Mock console.error to avoid noise in test output
      const consoleSpy = jest.spyOn(console, 'error').mockImplementation();

      // Since we're in test environment, we can't easily trigger real errors
      // But we can verify the method structure is correct
      const result = await service.makeRequest('test', 'GET', {});
      expect(result).toBeDefined();

      consoleSpy.mockRestore();
    });
  });
});
