/**
 * Copyright IBM Corp. 2024, 2025
 */
import axios from 'axios';
import { AxiosClient } from '../../../src/engine/protocol/axios-client.js';

jest.mock('axios');
const mockedAxios = axios as any as jest.Mock;

describe('AxiosClient', () => {
  let client: AxiosClient;

  beforeEach(() => {
    client = new AxiosClient();
    jest.clearAllMocks();
  });

  it('makes a successful GET request', async () => {
    mockedAxios.mockResolvedValueOnce({ data: { success: true } });

    const result = await client.request(
      {
        method: 'GET',
        url: 'http://example.com',
        headers: { 'content-type': 'application/json' },
      },
      false,
    );

    expect(mockedAxios).toHaveBeenCalledTimes(1);
    expect(result).toEqual({ data: { success: true } });
  });

  it('makes a successful POST call with formUploading request', async () => {
    mockedAxios.mockResolvedValueOnce({ data: { success: true } });

    const result = await client.request(
      {
        method: 'POST',
        url: 'http://example.com',
        headers: { 'content-type': 'application/octet-stream' },
      },
      true,
    );

    expect(mockedAxios).toHaveBeenCalledTimes(1);
    expect(result).toEqual({ data: { success: true } });
  });

  it('no retries on ECONNABORTED error', async () => {
    await client.request(
      {
        method: 'GET',
        url: 'http://example.com',
      },
      false,
    );

    expect(mockedAxios).toHaveBeenCalledTimes(1);
  });

  it('adds bearer token to headers', async () => {
    mockedAxios.mockResolvedValueOnce({ data: 'ok' });

    await client.request(
      {
        method: 'GET',
        url: 'http://example.com',
        headers: {
          Authorization: 'Bearer abc123',
        },
      },
      false,
    );

    const config = mockedAxios.mock.calls[0][0];
    expect(config.headers['Authorization']).toBe('Bearer abc123');
  });

  it('adds basic auth to headers', async () => {
    await client.request(
      {
        method: 'GET',
        url: 'http://example.com',
        headers: {
          Authorization: `Basic Zm9vOmJhcg==`,
        },
      },
      false,
    );

    const config = mockedAxios.mock.calls[0][0];
    expect(config).toEqual(
      expect.objectContaining({
        headers: expect.objectContaining({
          Authorization: 'Basic Zm9vOmJhcg==',
        }),
      }),
    );
  });

  it('should handle proxy settings', async () => {
    await client.request(
      {
        method: 'GET',
        url: 'http://example.com',
        proxy: {
          host: '127.0.0.1',
          port: 8080,
        },
      },
      false,
    );

    const config = mockedAxios.mock.calls[0][0];
    expect(config.proxy).toEqual({ host: '127.0.0.1', port: 8080 });
  });

  it('should support timeout and httpsAgent settings', async () => {
    mockedAxios.mockResolvedValueOnce({ data: 'ok' });
    await client.request(
      {
        method: 'GET',
        url: 'https://secure.com',
        timeout: 2000,
      },
      false,
    );

    const config = mockedAxios.mock.calls[0][0];
    expect(config.timeout).toBe(2000);
    expect(config).toEqual(
      expect.objectContaining({
        httpsAgent: expect.any(Object),
      }),
    );
  });

  it('should return agent with rejectUnauthorized = true when validateSSL is true', () => {
    const config = client['getAgentConfig'](true); // access private method via bracket notation
    expect(config).toHaveProperty('httpsAgent');
    expect(config.httpsAgent.options.rejectUnauthorized).toBe(true);
  });

  it('should return agent with rejectUnauthorized = false when validateSSL is false', () => {
    const config = client['getAgentConfig'](false);
    expect(config).toHaveProperty('httpsAgent');
    expect(config.httpsAgent.options.rejectUnauthorized).toBe(false);
  });
});
