/**
 * @jest-environment node
 */

import nock from 'nock';
import { expect } from '@jest/globals';
import { DeepSourceClient } from '../deepsource.js';
import { ClassifiedError, ErrorCategory } from '../utils/errors.js';

describe('DeepSourceClient', () => {
  // Test variables
  const API_KEY = 'test-api-key';
  let client: DeepSourceClient;

  // Setup client and nock interceptors
  beforeEach(() => {
    client = new DeepSourceClient(API_KEY);
    nock.disableNetConnect();
  });

  // Clean up after each test
  afterEach(() => {
    nock.cleanAll();
    nock.enableNetConnect();
  });

  describe('Error handling', () => {
    describe('HTTP Status Error Handling', () => {
      // Test server errors (500+)
      it('should handle server errors (500+) correctly', async () => {
        nock('https://api.deepsource.io')
          .post('/graphql/')
          .reply(500, { message: 'Internal Server Error' });

        try {
          await client.listProjects();
          expect(true).toBe(false); // This will fail if the code reaches here
        } catch (error) {
          expect(error).toBeInstanceOf(Error);
          const classifiedError = error as ClassifiedError;
          expect(classifiedError.category).toBe(ErrorCategory.SERVER);
          expect(classifiedError.message).toContain('Server error (500)');
          expect(classifiedError.originalError).toBeDefined();
        }
      });

      // Test 404 Not Found errors
      it('should handle 404 not found errors correctly', async () => {
        nock('https://api.deepsource.io').post('/graphql/').reply(404, { message: 'Not Found' });

        try {
          await client.listProjects();
          expect(true).toBe(false); // This will fail if the code reaches here
        } catch (error) {
          expect(error).toBeInstanceOf(Error);
          const classifiedError = error as ClassifiedError;
          expect(classifiedError.category).toBe(ErrorCategory.NOT_FOUND);
          expect(classifiedError.message).toContain('Not found (404)');
          expect(classifiedError.originalError).toBeDefined();
        }
      });

      // Test other 4xx client errors
      it('should handle other 4xx client errors correctly', async () => {
        nock('https://api.deepsource.io')
          .post('/graphql/')
          .reply(400, { message: 'Bad Request' }, { 'Content-Type': 'application/json' });

        try {
          await client.listProjects();
          expect(true).toBe(false); // This will fail if the code reaches here
        } catch (error) {
          expect(error).toBeInstanceOf(Error);
          const classifiedError = error as ClassifiedError;
          expect(classifiedError.category).toBe(ErrorCategory.CLIENT);
          expect(classifiedError.message).toContain('Client error (400)');
          expect(classifiedError.originalError).toBeDefined();
        }
      });

      // Test 4xx status without statusText
      it('should handle 4xx status without statusText correctly', async () => {
        nock('https://api.deepsource.io').post('/graphql/').reply(400, {});

        try {
          await client.listProjects();
          expect(true).toBe(false); // This will fail if the code reaches here
        } catch (error) {
          expect(error).toBeInstanceOf(Error);
          const classifiedError = error as ClassifiedError;
          expect(classifiedError.category).toBe(ErrorCategory.CLIENT);
          // Accept either "Bad Request" or "Bad request" since the error message can vary
          expect(classifiedError.message.toLowerCase()).toContain(
            'client error (400): bad request'
          );
          expect(classifiedError.originalError).toBeDefined();
        }
      });
    });

    describe('Generic Error Handling', () => {
      it('should handle standard Error objects', async () => {
        // Mock Axios to throw a standard Error
        nock('https://api.deepsource.io')
          .post('/graphql/')
          .replyWithError(new Error('Test generic error'));

        await expect(client.listProjects()).rejects.toThrow(
          'DeepSource API error: Test generic error'
        );
      });

      it('should handle string error messages', async () => {
        // Mock Axios to throw a string error message
        nock('https://api.deepsource.io').post('/graphql/').replyWithError('Not an Error object');

        await expect(client.listProjects()).rejects.toThrow(
          'DeepSource API error: Not an Error object'
        );
      });
    });
  });
});
