/**
 * Copyright IBM Corp. 2024, 2025
 */
import axios from 'axios';
import https from 'https';
import {
  fetchCaptureId,
  fetchTraceAndCatalogData,
} from '../../src/helpers/trace-helper.js';

// Mock axios
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

// Mock https
jest.mock('https', () => ({
  Agent: jest.fn().mockImplementation(() => ({})),
}));

describe('trace-helper', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    // Mock axios.create to return an object with post and get methods
    mockedAxios.create.mockReturnValue({
      post: jest.fn(),
      get: jest.fn(),
    } as any);
  });

  describe('fetchCaptureId', () => {
    const mockReqBody = JSON.stringify({
      url: 'https://api.example.com',
      apiName: 'test-api',
      orgName: 'test-org',
      catalogName: 'test-catalog',
      gatewayName: 'test-gateway',
    });

    const mockHeaders = {
      'authorization': 'Bearer test-token',
    };

    it('should fetch capture ID successfully', async () => {
      // Mock response data
      const mockResponseData = {
        captureId: 'test-capture-id',
        status: 'success',
      };

      // Setup axios post mock
      const mockPost = jest.fn().mockResolvedValue({
        data: mockResponseData,
      });
      mockedAxios.create.mockReturnValue({
        post: mockPost,
      } as any);

      // Call the function
      const result = await fetchCaptureId(mockReqBody, mockHeaders);

      // Assertions
      expect(https.Agent).toHaveBeenCalledWith({
        rejectUnauthorized: false,
      });
      expect(mockedAxios.create).toHaveBeenCalledWith({
        httpsAgent: expect.any(Object),
        baseURL: 'https://api.example.com',
      });

      // Verify the post call
      expect(mockPost).toHaveBeenCalledWith(
        'https://api.example.com/api/catalogs/test-org/test-catalog/configured-gateway-services/test-gateway/assembly-debug',
        { FilterByAPI: 'test-api' },
        {
          headers: {
            Authorization: 'Bearer test-token',
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
        },
      );

      // Verify the result
      expect(result).toEqual(mockResponseData);
    });

    it('should return null when token is not provided', async () => {
      // Call the function without token
      const result = await fetchCaptureId(mockReqBody, {});

      // Assertions
      expect(mockedAxios.create).not.toHaveBeenCalled();
      expect(result).toBeNull();
    });

    it('should handle errors and return null', async () => {
      // Setup axios post mock to throw error
      const mockPost = jest.fn().mockRejectedValue(new Error('Network error'));
      mockedAxios.create.mockReturnValue({
        post: mockPost,
      } as any);

      // Mock console.error
      console.error = jest.fn();

      // Call the function
      const result = await fetchCaptureId(mockReqBody, mockHeaders);

      // Assertions
      expect(console.error).toHaveBeenCalledWith('err ==> ', expect.any(Error));
      expect(result).toBeNull();
    });
  });

  describe('fetchTraceAndCatalogData', () => {
    const mockReqBody = JSON.stringify({
      url: 'https://api.example.com',
      orgName: 'test-org',
      catalogName: 'test-catalog',
      gatewayName: 'test-gateway',
      captureId: 'test-capture-id',
      transactionId: 'test-transaction-id',
      orgId: 'test-org-id',
      apiName: 'test-api',
      apiVersion: '1.0.0',
    });

    const mockHeaders = {
      'authorization': 'Bearer test-token',
    };

    it('should fetch trace and catalog data successfully', async () => {
      // Mock response data
      const mockTraceData = {
        id: 'trace-123',
        details: 'Sample trace data',
      };

      const mockCatalogData = {
        id: 'catalog-456',
        name: 'Test API',
        version: '1.0.0',
      };

      // Setup axios get mock for trace data
      const mockGet = jest
        .fn()
        .mockResolvedValueOnce({ data: mockTraceData }) // First call for trace data
        .mockResolvedValueOnce({ data: { catalog_api: mockCatalogData } }); // Second call for catalog data

      mockedAxios.create.mockReturnValue({
        get: mockGet,
      } as any);

      // Call the function
      const result = await fetchTraceAndCatalogData(mockReqBody, mockHeaders);

      // Assertions
      expect(https.Agent).toHaveBeenCalledWith({
        rejectUnauthorized: false,
      });

      // Verify the get calls
      expect(mockGet).toHaveBeenCalledWith(
        'https://api.example.com/api/catalogs/test-org/test-catalog/configured-gateway-services/test-gateway/assembly-debug/test-capture-id/transaction/test-transaction-id',
        {
          headers: {
            Authorization: 'Bearer test-token',
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
        },
      );

      expect(mockGet).toHaveBeenCalledWith(
        "https://api.example.com/api/catalogs/test-org-id/test-catalog/apis/test-api/1.0.0?fields=add(catalog_api,product_urls)'",
        {
          headers: {
            Authorization: 'Bearer test-token',
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
        },
      );

      // Verify the result
      expect(result).toEqual({
        traceData: mockTraceData,
        catalogData: mockCatalogData,
      });
    });

    it('should handle missing transaction ID and fetch it', async () => {
      // Mock request body without transaction ID
      const reqBodyWithoutTransId = JSON.stringify({
        url: 'https://api.example.com',
        orgName: 'test-org',
        catalogName: 'test-catalog',
        gatewayName: 'test-gateway',
        captureId: 'test-capture-id',
        orgId: 'test-org-id',
        apiName: 'test-api',
        apiVersion: '1.0.0',
      });

      // Mock response data
      const mockTransactionId = 'fetched-transaction-id';
      const mockTraceData = {
        id: 'trace-123',
        details: 'Sample trace data',
      };

      const mockCatalogData = {
        id: 'catalog-456',
        name: 'Test API',
        version: '1.0.0',
      };

      // Setup axios get mock
      const mockGet = jest
        .fn()
        .mockResolvedValueOnce({ Probe: { ProbeID: mockTransactionId } }) // First call to fetch transaction ID (no data wrapper)
        .mockResolvedValueOnce({ data: mockTraceData }) // Second call for trace data
        .mockResolvedValueOnce({ data: { catalog_api: mockCatalogData } }); // Third call for catalog data

      mockedAxios.create.mockReturnValue({
        get: mockGet,
      } as any);

      // Call the function
      const result = await fetchTraceAndCatalogData(
        reqBodyWithoutTransId,
        mockHeaders,
      );

      // Assertions for transaction ID fetch
      expect(mockGet).toHaveBeenCalledWith(
        'https://api.example.com/api/catalogs/test-org/test-catalog/configured-gateway-services/test-gateway/assembly-debug/test-capture-id/transaction',
        {
          headers: {
            Authorization: 'Bearer test-token',
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
        },
      );

      // Verify the result
      expect(result).toEqual({
        traceData: mockTraceData,
        catalogData: mockCatalogData,
      });
    });

    it('should handle errors in fetchTraceData and return null', async () => {
      // Setup axios get mock to throw error for trace data
      const mockGet = jest
        .fn()
        .mockRejectedValueOnce(new Error('Network error')) // Error for trace data
        .mockResolvedValueOnce({
          data: { catalog_api: { id: 'catalog-456' } },
        }); // Success for catalog data

      mockedAxios.create.mockReturnValue({
        get: mockGet,
      } as any);

      // Mock console.error
      console.error = jest.fn();

      // Call the function
      const result = await fetchTraceAndCatalogData(mockReqBody, mockHeaders);

      // Assertions
      expect(console.error).toHaveBeenCalledWith(
        'fetch trace error',
        expect.any(Error),
      );
      expect(result).toEqual({
        traceData: null,
        catalogData: { id: 'catalog-456' },
      });
    });

    it('should handle errors in fetchCatalogData and return null', async () => {
      // Setup axios get mock
      const mockGet = jest
        .fn()
        .mockResolvedValueOnce({ data: { id: 'trace-123' } }) // Success for trace data
        .mockRejectedValueOnce(new Error('Catalog error')); // Error for catalog data

      mockedAxios.create.mockReturnValue({
        get: mockGet,
      } as any);

      // Mock console.error
      console.error = jest.fn();

      // Call the function
      const result = await fetchTraceAndCatalogData(mockReqBody, mockHeaders);

      // Assertions
      expect(console.error).toHaveBeenCalledWith(
        'catalog error ==>',
        expect.any(Error),
      );
      expect(result).toEqual({
        traceData: { id: 'trace-123' },
        catalogData: null,
      });
    });
  });
});
