/**
 * Copyright IBM Corp. 2024, 2025
 */
import { TestExecutionReport } from '../../../src/engine/reporting/test-execution-report.js';
import { LogWrapper } from '../../../src/service/log-wrapper.js';
import {
  filterSensitiveData,
  generateCSV,
  generatePDF,
} from '../../../src/helpers/helper.js';
import { AssertConstants } from '../../../src/constants/assertConstants.js';

jest.mock('../../../src/service/log-wrapper.ts');
jest.mock('../../../src/helpers/helper.ts', () => ({
  filterSensitiveData: jest.fn((item) => item),
  generateCSV: jest.fn((data) => `CSV_OUTPUT:${JSON.stringify(data)}`),
  generatePDF: jest.fn((data) => `PDF_OUTPUT:${JSON.stringify(data)}`),
}));

describe('TestExecutionReport', () => {
  let report: TestExecutionReport;

  beforeEach(() => {
    jest.clearAllMocks();
    report = new TestExecutionReport();
  });

  describe('collectReport', () => {
    it('should log info when successful', async () => {
      const sampleAssertion = {
        assertion: 'sample assertion key',
        action: AssertConstants.equals_action,
        error: {
          name: 'sample name',
          index: 1,
          test: 'sample test',
          message: 'sample message',
          stack: 'sample stack',
        },
        skipped: false,
        key: AssertConstants.equals_action,
      };
      const mockAssertionSummary = [
        {
          request: 'sample',
          assertions: [sampleAssertion],
        },
      ];

      const result = report.collectReport(
        'sample collection id',
        'Test file',
        mockAssertionSummary,
        [],
        2000,
        4000,
      );

      expect(LogWrapper.logInfo).toHaveBeenCalledWith('0215', 'Test file');
      expect(result).toEqual({
        id: 'sample collection id',
        name: 'Test file Collection',
        timestamp: 4000,
        totalPass: 0,
        totalFail: 1,
        status: 'finished',
        startedAt: 2000,
        totalTime: 2000,
        results: [],
      });
    });

    it('should format execution results correctly', () => {
      const mockExecution = {
        id: '1',
        itemName: 'Test Item',
        request: {
          endpoint: 'http://example.com',
          method: 'GET',
          headers: [],
        },
        response: {
          status: 200,
          statusText: 'OK',
          responseTime: 100,
          responseSize: 500,
          data: 'Response body',
        },
        assertions: [
          { assertion: 'should return 200', error: null },
          {
            assertion: 'should contain header',
            error: { message: 'Missing header' },
          },
        ],
        startedAt: 1000,
        completedAt: 2000,
      };

      const result = (report as any).getExecutionResults([mockExecution]);

      expect(result).toEqual([
        {
          id: '1',
          name: 'Test Item',
          url: 'http://example.com',
          method: 'GET',
          header: [],
          time: 1000,
          responseCode: {
            code: 200,
            name: 'OK',
            time: 100,
            size: 500,
          },
          responseHeaders: null,
          response: 'Response body',
          allTests: [
            {
              'should return 200': {
                status: true,
              },
            },
            {
              'should contain header': {
                error: {
                  message: 'Missing header',
                },
                status: false,
              },
            },
          ],
        },
      ]);
    });
    it('should format execution results correctly - default response code', () => {
      const mockExecution = {
        id: '1',
        itemName: 'Test Item',
        request: {
          endpoint: 'http://example.com',
          method: 'GET',
          headers: [],
        },
        response: {
          data: 'Response body',
        },
        assertions: [
          { assertion: 'should return 200', error: null },
          {
            assertion: 'should contain header',
            error: { message: 'Missing header' },
          },
        ],
      };

      const result = (report as any).getExecutionResults([mockExecution]);

      expect(result).toEqual([
        {
          id: '1',
          name: 'Test Item',
          url: 'http://example.com',
          method: 'GET',
          header: [],
          time: 0,
          responseCode: {
            code: 408,
            name: 'Request Timed out',
            time: 0,
            size: 0,
          },
          responseHeaders: null,
          response: 'Response body',
          allTests: [
            {
              'should return 200': {
                status: true,
              },
            },
            {
              'should contain header': {
                error: {
                  message: 'Missing header',
                },
                status: false,
              },
            },
          ],
        },
      ]);
    });
    it('should format execution results correctly for not having response body', () => {
      const mockExecution = {
        id: '1',
        itemName: 'Test Item',
        request: {
          endpoint: 'http://example.com',
          method: 'GET',
          headers: [],
        },
        response: {
          status: 200,
          statusText: 'OK',
          responseTime: 100,
          responseSize: 500,
        },
        assertions: [
          { assertion: 'should return 200', error: null },
          {
            assertion: 'should contain header',
            error: { message: 'Missing header' },
          },
        ],
        startedAt: 1000,
        completedAt: 2000,
      };

      const result = (report as any).getExecutionResults([mockExecution]);

      expect(result).toEqual([
        {
          id: '1',
          name: 'Test Item',
          url: 'http://example.com',
          method: 'GET',
          header: [],
          time: 1000,
          responseCode: {
            code: 200,
            name: 'OK',
            time: 100,
            size: 500,
          },
          responseHeaders: null,
          response: 'Response unavailable',
          allTests: [
            {
              'should return 200': {
                status: true,
              },
            },
            {
              'should contain header': {
                error: {
                  message: 'Missing header',
                },
                status: false,
              },
            },
          ],
        },
      ]);
    });
    it('should format execution results correctly for not having response stream', () => {
      const mockExecution = {
        id: '1',
        itemName: 'Test Item',
        request: {
          endpoint: 'http://example.com',
          method: 'GET',
          headers: [],
        },
        response: {
          status: 200,
          statusText: 'OK',
          responseTime: 100,
          responseSize: 500,
          stream:
            '<Buffer 7b 22 73 74 61 74 75 73 22 3a 22 73 75 63 63 65 73 73 22 7d>',
        },
        assertions: [
          { assertion: 'should return 200', error: null },
          {
            assertion: 'should contain header',
            error: { message: 'Missing header' },
          },
        ],
        startedAt: 1000,
        completedAt: 2000,
      };

      const result = (report as any).getExecutionResults([mockExecution]);

      expect(result).toEqual([
        {
          id: '1',
          name: 'Test Item',
          url: 'http://example.com',
          method: 'GET',
          header: [],
          time: 1000,
          responseCode: {
            code: 200,
            name: 'OK',
            time: 100,
            size: 500,
          },
          responseHeaders: null,
          response:
            '<Buffer 7b 22 73 74 61 74 75 73 22 3a 22 73 75 63 63 65 73 73 22 7d>',
          allTests: [
            {
              'should return 200': {
                status: true,
              },
            },
            {
              'should contain header': {
                error: {
                  message: 'Missing header',
                },
                status: false,
              },
            },
          ],
        },
      ]);
    });
  });

  describe('getReport', () => {
    it('applies filterSensitiveData and calls generateCSV for CSV format', () => {
      const dummyFilteredSummaries = [
        {
          id: 's1',
          name: 'S1',
          timeStamp: 'sampleTime',
          totalPass: 10,
          status: 'success',
          results: [],
        },
        {
          id: 's2',
          name: 'S2',
          timeStamp: 'testTime',
          totalPass: 10,
          status: 'failed',
          results: [],
        },
      ];

      const output = report.getReport(dummyFilteredSummaries, 'CSV');

      expect(filterSensitiveData).toHaveBeenCalledTimes(2);
      expect(generateCSV).toHaveBeenCalledWith([
        {
          id: 's1',
          name: 'S1',
          timeStamp: 'sampleTime',
          totalPass: 10,
          status: 'success',
          results: [],
        },
        {
          id: 's2',
          name: 'S2',
          timeStamp: 'testTime',
          totalPass: 10,
          status: 'failed',
          results: [],
        },
      ]);

      expect(output).toBe(
        `CSV_OUTPUT:${JSON.stringify([
          {
            id: 's1',
            name: 'S1',
            timeStamp: 'sampleTime',
            totalPass: 10,
            status: 'success',
            results: [],
          },
          {
            id: 's2',
            name: 'S2',
            timeStamp: 'testTime',
            totalPass: 10,
            status: 'failed',
            results: [],
          },
        ])}`,
      );
    });

    it('applies filterSensitiveData and calls generatePDF for PDF format', () => {
      const dummyFilteredSummaries = [
        {
          id: 's1',
          name: 'S1',
          timeStamp: 'sampleTime',
          totalPass: 10,
          status: 'success',
          results: [],
        },
      ];

      const output = report.getReport(dummyFilteredSummaries as any, 'PDF');

      expect(filterSensitiveData).toHaveBeenCalledTimes(1);
      expect(generatePDF).toHaveBeenCalledWith([
        {
          id: 's1',
          name: 'S1',
          timeStamp: 'sampleTime',
          totalPass: 10,
          status: 'success',
          results: [],
        },
      ]);
      expect(output).toBe(
        `PDF_OUTPUT:${JSON.stringify([
          {
            id: 's1',
            name: 'S1',
            timeStamp: 'sampleTime',
            totalPass: 10,
            status: 'success',
            results: [],
          },
        ])}`,
      );
    });
  });
});
