/**
 * Copyright IBM Corp. 2024, 2025
 */
import { TestManager } from '../../src/index.js';
import path from 'path';
import fs from 'fs';
import { TestRunner } from '../../src/engine/execution/test-runner.js';
jest.mock('../../src/service/log-wrapper.js');
jest.mock('../../src/helpers/trace-helper.js', () => ({
  fetchTraceAndCatalogData: jest.fn(),
  fetchCaptureId: jest.fn()
}));

jest.mock('@apic/studio-logger', () => ({
  LoggerConfig: {
    isLoggerEnabled: jest.fn(),
  },
}));

jest.mock('../../src/engine/execution/test-runner.js', () => ({
  TestRunner: jest.fn().mockImplementation(() => ({
    run: jest.fn(),
  })),
}));

describe('test manager tests', () => {
  const MockTestResponseValid = {
    id: '6d619b61-1de3-473c-a508-948f872ce090',
    name: 'PetPostTest Collection',
    timestamp: 1750224392789,
    totalPass: 8,
    status: 'finished',
    startedAt: 1750224389787,
    totalFail: 0,
    totalTime: 3002,
    results: [
      {
        id: '',
        name: 'POST /pet',
        url: 'https://petstore.swagger.io/v2/pet',
        method: 'POST',
        header: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
        time: 1539,
        responseCode: {
          code: 200,
          name: 'OK',
          time: 1538,
          size: 0,
        },
        response: {
          id: 9223372036854739000,
          category: {
            id: 0,
            name: 'string',
          },
          name: 'doggie',
          photoUrls: ['string'],
          tags: [
            {
              id: 0,
              name: 'string',
            },
          ],
          status: 'available',
        },
        responseHeaders: [
          {
            key: 'date',
            value: 'Wed, 18 Jun 2025 05:26:31 GMT',
          },
          {
            key: 'content-type',
            value: 'application/json',
          },
          {
            key: 'transfer-encoding',
            value: 'chunked',
          },
          {
            key: 'connection',
            value: 'close',
          },
          {
            key: 'access-control-allow-origin',
            value: '*',
          },
          {
            key: 'access-control-allow-methods',
            value: 'GET, POST, DELETE, PUT',
          },
          {
            key: 'access-control-allow-headers',
            value: 'Content-Type, api_key, Authorization',
          },
          {
            key: 'server',
            value: 'Jetty(9.2.9.v20150224)',
          },
        ],
        allTests: [
          {
            'Validate Response time': {
              status: true,
              actualValue: 1538,
              expectedValue: 3,
              action: 'notEquals',
            },
          },
        ],
      },
    ],
  };

  const mockedResponseInvalidOutputvariable = {
    id: '1fb34fdd-989d-4a4f-b821-b2c9b776694b',
    name: 'PetPostTest Collection',
    timestamp: 1750227902380,
    totalPass: 0,
    status: 'finished',
    startedAt: 1750227902377,
    totalFail: 0,
    totalTime: 3,
    results: [],
  };

  const mockedResponseValidOutputVariable = {
    id: 'd4bf0632-86f2-42dc-b7f6-9485815f553a',
    name: 'PetPostTest Collection',
    timestamp: 1750233105313,
    totalPass: 3,
    status: 'finished',
    startedAt: 1750233104110,
    totalFail: 0,
    totalTime: 1203,
    results: [
      {
        id: '',
        name: 'GET /pet/9223372036854740000',
        url: 'https://petstore.swagger.io/v2/pet/9223372036854740000',
        method: 'GET',
        header: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
        time: 1202,
        responseCode: {
          code: 200,
          name: 'OK',
          time: 1201,
          size: 0,
        },
        response: {
          id: 9223372036854740000,
          name: 'UPDATED DOGGIE',
          photoUrls: ['https://example.com/dog.jpg'],
          tags: [],
        },
        responseHeaders: [
          {
            key: 'date',
            value: 'Wed, 18 Jun 2025 07:51:45 GMT',
          },
          {
            key: 'content-type',
            value: 'application/json',
          },
          {
            key: 'transfer-encoding',
            value: 'chunked',
          },
          {
            key: 'connection',
            value: 'close',
          },
          {
            key: 'access-control-allow-origin',
            value: '*',
          },
          {
            key: 'access-control-allow-methods',
            value: 'GET, POST, DELETE, PUT',
          },
          {
            key: 'access-control-allow-headers',
            value: 'Content-Type, api_key, Authorization',
          },
          {
            key: 'server',
            value: 'Jetty(9.2.9.v20150224)',
          },
        ],
        allTests: [
          {
            'Validate Response time': {
              status: true,
              actualValue: 1201,
              expectedValue: 3,
              action: 'notEquals',
            },
          },
          {
            'Validate data id': {
              status: true,
              actualValue: 9223372036854740000,
              expectedValue: 1000,
              action: 'greaterThan',
            },
          },
          {
            'Custom chai assertion': {
              status: true,
              actualValue: 9223372036854740000,
              expectedValue: 2,
              action: 'notEquals',
            },
          },
        ],
      },
    ],
  };

  const mockedResponseInvalidOutputVariableAssertion = {
    id: '7134489e-4481-4fc9-9401-51f03bbcf550',
    name: 'PetPostTest Collection',
    timestamp: 1750237726325,
    totalPass: 2,
    status: 'finished',
    startedAt: 1750237725089,
    totalFail: 1,
    totalTime: 1236,
    results: [
      {
        id: '',
        name: 'GET /pet/9223372036854741000',
        url: 'https://petstore.swagger.io/v2/pet/9223372036854741000',
        method: 'GET',
        header: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
        time: 1235,
        responseCode: {
          code: 200,
          name: 'OK',
          time: 1235,
          size: 0,
        },
        response: {
          id: 9223372036854741000,
          category: {
            id: 0,
            name: 'string',
          },
          name: 'doggie',
          photoUrls: ['string'],
          tags: [
            {
              id: 0,
              name: 'string',
            },
          ],
          status: 'string',
        },
        responseHeaders: [
          {
            key: 'date',
            value: 'Wed, 18 Jun 2025 09:08:46 GMT',
          },
          {
            key: 'content-type',
            value: 'application/json',
          },
          {
            key: 'transfer-encoding',
            value: 'chunked',
          },
          {
            key: 'connection',
            value: 'close',
          },
          {
            key: 'access-control-allow-origin',
            value: '*',
          },
          {
            key: 'access-control-allow-methods',
            value: 'GET, POST, DELETE, PUT',
          },
          {
            key: 'access-control-allow-headers',
            value: 'Content-Type, api_key, Authorization',
          },
          {
            key: 'server',
            value: 'Jetty(9.2.9.v20150224)',
          },
        ],
        allTests: [
          {
            'Validate Response time': {
              status: true,
              actualValue: 1235,
              expectedValue: 2,
              action: 'notEquals',
            },
          },
          {
            'Validate data id': {
              status: false,
              error: {
                name: 'Error',
                test: 'Validate data id',
                message:
                  "Cannot resolve path 'payload.data.ids' – 'ids' not found.",
                stack:
                  "Error: Cannot resolve path 'payload.data.ids' – 'ids' not found.\n    at ContextManager.resolvePath (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/variable-context-manager/context-manager.js:137:23)\n    at ContextManager.resolveValue (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/variable-context-manager/context-manager.js:110:25)\n    at ContextManager.resolve (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/variable-context-manager/context-manager.js:86:21)\n    at AssertionEngine.resolveValue (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/assertion/assertion.engine.js:75:20)\n    at AssertionEngine.assert (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/assertion/assertion.engine.js:22:42)\n    at TestRunner.run (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/execution/test-runner.js:35:68)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async Promise.allSettled (index 0)\n    at async TestManager.processFile (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/managers/test.manager.js:210:35)\n    at async file:///Users/anulal/Developer/api-studio/packages/studio-server/dist/controllers/test.controller.js:36:25",
              },
              action: 'equals',
            },
          },
          {
            'Custom chai assertion': {
              status: true,
              actualValue: 9223372036854741000,
              expectedValue: 2,
              action: 'notEquals',
            },
          },
        ],
      },
    ],
  };

  it('should return response for valid asset', async () => {
    (TestRunner as jest.Mock).mockImplementation(() => {
      return {
        run: () => MockTestResponseValid,
      };
    });
    const obj = new TestManager();
    const zipFilePath = path.resolve(__dirname, '../assets/valid-asset.zip');
    const zipBuffer = fs.readFileSync(zipFilePath);
    const result = await obj.processFile(zipBuffer);
    expect(Array.isArray(result)).toBe(true);
    expect(result[0]).toEqual(MockTestResponseValid);
  });

  it('should return empty result if output variable is not defined in test file', async () => {
    (TestRunner as jest.Mock).mockImplementation(() => {
      return {
        run: () => mockedResponseInvalidOutputvariable,
      };
    });
    const obj = new TestManager();
    const zipFilePath = path.resolve(
      __dirname,
      '../assets/invalidOutputVariable.zip',
    );
    const zipBuffer = fs.readFileSync(zipFilePath);
    const result = await obj.processFile(zipBuffer);
    expect(Array.isArray(result)).toBe(true);
    expect(result).toEqual([mockedResponseInvalidOutputvariable]);
  });

  it('should return response if output variable is  defined in test file', async () => {
    (TestRunner as jest.Mock).mockImplementation(() => {
      return {
        run: () => mockedResponseValidOutputVariable,
      };
    });
    const obj = new TestManager();
    const zipFilePath = path.resolve(__dirname, '../assets/valid-asset.zip');
    const zipBuffer = fs.readFileSync(zipFilePath);
    const result = await obj.processFile(zipBuffer);
    expect(result).toEqual([mockedResponseValidOutputVariable]);
  });

  it('should return null if output variable is not defined in Assertion file', async () => {
    (TestRunner as jest.Mock).mockImplementation(() => {
      return {
        run: () => mockedResponseInvalidOutputVariableAssertion,
      };
    });
    const obj = new TestManager();
    const zipFilePath = path.resolve(
      __dirname,
      '../assets/InvalidOutputVariableInAssertion.zip',
    );
    const zipBuffer = fs.readFileSync(zipFilePath);
    const result = await obj.processFile(zipBuffer);
    expect(Array.isArray(result)).toBe(true);
    expect(result).toEqual([mockedResponseInvalidOutputVariableAssertion]);
  });

  describe('processTrace', () => {
    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should call fetchTraceAndCatalogData with the correct parameters', async () => {
      // Mock the fetchTraceAndCatalogData function
      const mockTraceData = {
        traceData: { id: 'trace-123', details: 'Sample trace data' },
        catalogData: { id: 'catalog-456', name: 'Test API' }
      };
      
      const traceHelperModule = require('../../src/helpers/trace-helper.js');
      traceHelperModule.fetchTraceAndCatalogData.mockResolvedValue(mockTraceData);
      
      // Create test data
      const reqBody = JSON.stringify({
        url: 'https://api.example.com',
        apiName: 'test-api'
      });
      
      const reqHeader = {
        'x-access-token': 'test-token'
      };
      
      // Call the method
      const testManager = new TestManager();
      const result = await testManager.processTrace(reqBody, reqHeader);
      
      // Assertions
      expect(traceHelperModule.fetchTraceAndCatalogData).toHaveBeenCalledWith(reqBody, reqHeader);
      expect(result).toEqual(mockTraceData);
    });

    it('should handle errors from fetchTraceAndCatalogData', async () => {
      // Mock the fetchTraceAndCatalogData function to throw an error
      const mockError = new Error('Failed to fetch trace data');
      const traceHelperModule = require('../../src/helpers/trace-helper.js');
      traceHelperModule.fetchTraceAndCatalogData.mockRejectedValue(mockError);
      
      // Create test data
      const reqBody = JSON.stringify({
        url: 'https://api.example.com',
        apiName: 'test-api'
      });
      
      const reqHeader = {
        'x-access-token': 'test-token'
      };
      
      // Call the method
      const testManager = new TestManager();
      
      // Assert that the error is propagated
      await expect(testManager.processTrace(reqBody, reqHeader)).rejects.toThrow('Failed to fetch trace data');
    });

    it('should return null when fetchTraceAndCatalogData returns null', async () => {
      // Mock the fetchTraceAndCatalogData function to return null
      const traceHelperModule = require('../../src/helpers/trace-helper.js');
      traceHelperModule.fetchTraceAndCatalogData.mockResolvedValue(null);
      
      // Create test data
      const reqBody = JSON.stringify({
        url: 'https://api.example.com',
        apiName: 'test-api'
      });
      
      const reqHeader = {
        'x-access-token': 'test-token'
      };
      
      // Call the method
      const testManager = new TestManager();
      const result = await testManager.processTrace(reqBody, reqHeader);
      
      // Assertions
      expect(result).toBeNull();
    });
  });

  describe('getCaptureId', () => {
    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should call fetchCaptureId with the correct parameters', async () => {
      // Mock the fetchCaptureId function
      const mockCaptureData = {
        captureId: 'capture-123',
        status: 'success'
      };
      
      const traceHelperModule = require('../../src/helpers/trace-helper.js');
      traceHelperModule.fetchCaptureId.mockResolvedValue(mockCaptureData);
      
      // Create test data
      const reqBody = JSON.stringify({
        url: 'https://api.example.com',
        apiName: 'test-api'
      });
      
      const reqHeader = {
        'x-access-token': 'test-token'
      };
      
      // Call the method
      const testManager = new TestManager();
      const result = await testManager.getCaptureId(reqBody, reqHeader);
      
      // Assertions
      expect(traceHelperModule.fetchCaptureId).toHaveBeenCalledWith(reqBody, reqHeader);
      expect(result).toEqual(mockCaptureData);
    });

    it('should handle errors from fetchCaptureId', async () => {
      // Mock the fetchCaptureId function to throw an error
      const mockError = new Error('Failed to fetch capture ID');
      const traceHelperModule = require('../../src/helpers/trace-helper.js');
      traceHelperModule.fetchCaptureId.mockRejectedValue(mockError);
      
      // Create test data
      const reqBody = JSON.stringify({
        url: 'https://api.example.com',
        apiName: 'test-api'
      });
      
      const reqHeader = {
        'x-access-token': 'test-token'
      };
      
      // Call the method
      const testManager = new TestManager();
      
      // Assert that the error is propagated
      await expect(testManager.getCaptureId(reqBody, reqHeader)).rejects.toThrow('Failed to fetch capture ID');
    });

    it('should return null when fetchCaptureId returns null', async () => {
      // Mock the fetchCaptureId function to return null
      const traceHelperModule = require('../../src/helpers/trace-helper.js');
      traceHelperModule.fetchCaptureId.mockResolvedValue(null);
      
      // Create test data
      const reqBody = JSON.stringify({
        url: 'https://api.example.com',
        apiName: 'test-api'
      });
      
      const reqHeader = {
        'x-access-token': 'test-token'
      };
      
      // Call the method
      const testManager = new TestManager();
      const result = await testManager.getCaptureId(reqBody, reqHeader);
      
      // Assertions
      expect(result).toBeNull();
    });
  });
});
