import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { UnauthorizedException } from '@nestjs/common';
import { LaravelApiService, LaravelApiRequest } from './laravel-api.service';

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

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        LaravelApiService,
        {
          provide: ConfigService,
          useValue: {
            get: jest.fn().mockReturnValue('http://localhost:8000'),
          },
        },
      ],
    }).compile();

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

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

  describe('makeAuthenticatedRequest', () => {
    it('should return mock data in test environment', async () => {
      const token = 'test-token';
      const request: LaravelApiRequest = {
        method: 'GET',
        endpoint: '/api/test',
        data: { test: 'data' },
      };

      const result = await service.makeAuthenticatedRequest(token, request);

      expect(result).toEqual({
        data: {
          source: 'Laravel API',
          endpoint: '/api/test',
          method: 'GET',
          data: { test: 'data' },
        },
        status: 200,
        headers: { 'content-type': 'application/json' },
      });
    });

    it('should handle request with params', async () => {
      const token = 'test-token';
      const request: LaravelApiRequest = {
        method: 'GET',
        endpoint: '/api/test',
        params: { page: 1, limit: 10 },
      };

      const result = await service.makeAuthenticatedRequest(token, request);

      expect(result.data).toHaveProperty('endpoint', '/api/test');
      expect(result.data).toHaveProperty('method', 'GET');
      expect(result.status).toBe(200);
    });

    it('should handle POST request with data', async () => {
      const token = 'test-token';
      const request: LaravelApiRequest = {
        method: 'POST',
        endpoint: '/api/users',
        data: { name: 'John Doe', email: 'john@example.com' },
      };

      const result = await service.makeAuthenticatedRequest(token, request);

      expect(result.data).toHaveProperty('endpoint', '/api/users');
      expect(result.data).toHaveProperty('method', 'POST');
      expect(result.data).toHaveProperty('data', {
        name: 'John Doe',
        email: 'john@example.com',
      });
    });

    it('should handle request with custom headers', async () => {
      const token = 'test-token';
      const request: LaravelApiRequest = {
        method: 'GET',
        endpoint: '/api/test',
        headers: { 'X-Custom-Header': 'custom-value' },
      };

      const result = await service.makeAuthenticatedRequest(token, request);

      expect(result.data).toHaveProperty('endpoint', '/api/test');
      expect(result.status).toBe(200);
    });
  });

  describe('validateTokenWithLaravel', () => {
    it('should validate token and return user data', async () => {
      const token = 'valid-token';

      const result = await service.validateTokenWithLaravel(token);

      expect(result).toEqual({
        source: 'Laravel API',
        endpoint: '/api/user',
        method: 'GET',
        data: undefined,
      });
    });

    it('should throw UnauthorizedException for invalid token', async () => {
      // Mock the makeAuthenticatedRequest to throw an error
      jest
        .spyOn(service, 'makeAuthenticatedRequest')
        .mockRejectedValue(new Error('Token validation failed'));

      const token = 'invalid-token';

      await expect(service.validateTokenWithLaravel(token)).rejects.toThrow(
        UnauthorizedException,
      );
    });
  });

  describe('getUserFromLaravel', () => {
    it('should get user data from Laravel API', async () => {
      const token = 'user-token';

      const result = await service.getUserFromLaravel(token);

      expect(result).toEqual({
        data: {
          source: 'Laravel API',
          endpoint: '/api/user',
          method: 'GET',
          data: undefined,
        },
        status: 200,
        headers: { 'content-type': 'application/json' },
      });
    });
  });

  describe('getClientFromLaravel', () => {
    it('should get client data from Laravel API', async () => {
      const token = 'client-token';

      const result = await service.getClientFromLaravel(token);

      expect(result).toEqual({
        data: {
          source: 'Laravel API',
          endpoint: '/api/client/me',
          method: 'GET',
          data: undefined,
        },
        status: 200,
        headers: { 'content-type': 'application/json' },
      });
    });
  });

  describe('proxyRequest', () => {
    it('should proxy GET request', async () => {
      const token = 'proxy-token';
      const endpoint = '/api/proxy-test';

      const result = await service.proxyRequest(token, 'GET', endpoint);

      expect(result.data).toHaveProperty('endpoint', endpoint);
      expect(result.data).toHaveProperty('method', 'GET');
      expect(result.status).toBe(200);
    });

    it('should proxy POST request with data', async () => {
      const token = 'proxy-token';
      const endpoint = '/api/proxy-test';
      const data = { test: 'proxy-data' };

      const result = await service.proxyRequest(token, 'POST', endpoint, data);

      expect(result.data).toHaveProperty('endpoint', endpoint);
      expect(result.data).toHaveProperty('method', 'POST');
      expect(result.data).toHaveProperty('data', data);
    });

    it('should proxy PUT request with data and params', async () => {
      const token = 'proxy-token';
      const endpoint = '/api/proxy-test';
      const data = { name: 'Updated Name' };
      const params = { id: 123 };

      const result = await service.proxyRequest(
        token,
        'PUT',
        endpoint,
        data,
        params,
      );

      expect(result.data).toHaveProperty('endpoint', endpoint);
      expect(result.data).toHaveProperty('method', 'PUT');
      expect(result.data).toHaveProperty('data', data);
    });

    it('should proxy PATCH request with headers', async () => {
      const token = 'proxy-token';
      const endpoint = '/api/proxy-test';
      const headers = { 'X-Custom': 'header-value' };

      const result = await service.proxyRequest(
        token,
        'PATCH',
        endpoint,
        undefined,
        undefined,
        headers,
      );

      expect(result.data).toHaveProperty('endpoint', endpoint);
      expect(result.data).toHaveProperty('method', 'PATCH');
    });

    it('should proxy DELETE request', async () => {
      const token = 'proxy-token';
      const endpoint = '/api/proxy-test/123';

      const result = await service.proxyRequest(token, 'DELETE', endpoint);

      expect(result.data).toHaveProperty('endpoint', endpoint);
      expect(result.data).toHaveProperty('method', 'DELETE');
    });
  });

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

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

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

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