/**
 * Copyright IBM Corp. 2024, 2025
 */
import axios from 'axios';
import FormData from 'form-data';
import {
  sendToGateway,
  validationManager,
  ParseAndFindResponse,
} from '../../src/service/gateway-service.js';

jest.mock('axios');

const mockAppend = jest.fn();
const mockGetHeaders = jest.fn().mockReturnValue({});

jest.mock('form-data', () => {
  return jest.fn().mockImplementation(() => ({
    append: mockAppend,
    getHeaders: mockGetHeaders,
  }));
});

jest.mock('@apic/studio-shared', () => {
  return {
    Logger: {
      debug: jest.fn(),
      info: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      log: jest.fn(),
    },
    IpcError: class IpcError extends Error {
      status: number;
      data: any;
      constructor(message: string, status: number, data?: any) {
        super(message);
        this.name = 'IpcError';
        this.status = status;
        this.data = data;
      }
    },
    GatewaysJson: {
      gateways: [],
      overwrite: true,
      skip: false,
    },
  };
});

describe('gateway-service', () => {
  const zipBuffer = Buffer.from('mock');
  const gatewayJson = { overwrite: true } as any;

  describe('sendToGateway', () => {
    beforeEach(() => {
      jest.clearAllMocks();
      (FormData as any).mockImplementation(() => ({
        append: jest.fn(),
        getHeaders: jest.fn().mockReturnValue({}),
      }));
    });

    it('should send successfully with basic auth', async () => {
      (axios.post as jest.Mock).mockResolvedValue({ status: 200, data: {} });
      await expect(
        sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
      ).resolves.toMatchObject({
        success: true,
        statusCode: 200,
        message: 'Api deployment Successful',
      });
    });

    it('should send successfully with bearer token and failed APIs in response', async () => {
      (axios.post as jest.Mock).mockResolvedValue({
        status: 200,
        data: {
          StudioResult: [
            {
              API: { status: 'Failed', explanation: 'Something bad happened' },
            },
          ],
        },
      });
      await expect(
        sendToGateway('http://example.com', '', 'token', true, zipBuffer, gatewayJson)
      ).resolves.toMatchObject({
        success: false,
        errors: expect.arrayContaining(['Something bad happened']),
        message: 'Api deployment Failed',
      });
    });

    it('should handle axios post failure without status (fallback to 404)', async () => {
      (axios.post as jest.Mock).mockRejectedValue({ message: 'Post failed' });
      await expect(
        sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
      ).resolves.toMatchObject({ success: false, statusCode: 404 });
    });

    it('should handle 401 response and provide error message in sendToGateway', async () => {
      (axios.post as jest.Mock).mockRejectedValue({
        message: 'Unauthorized',
        response: { status: 401 },
      });
      await expect(
        sendToGateway('http://example.com', 'user', 'wrongpass', false, zipBuffer, gatewayJson)
      ).resolves.toMatchObject({
        statusCode: 401,
        errors: expect.arrayContaining([
          'Unauthorized access: Please check your authentication details.',
        ]),
      });
    });

    it('should handle error with errorDetails in response', async () => {
      (axios.post as jest.Mock).mockRejectedValue({
        message: 'Post failed',
        response: {
          status: 500,
          data: {
            errorDetails: 'Detailed error message from gateway',
          },
        },
      });
      await expect(
        sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
      ).resolves.toMatchObject({
        success: false,
        statusCode: 500,
        message: 'Detailed error message from gateway',
        errors: ['Detailed error message from gateway'],
      });
    });

    it('should fallback to error.message when errorDetails is not present', async () => {
      (axios.post as jest.Mock).mockRejectedValue({
        message: 'Generic failure',
        response: {
          status: 500,
          data: {},
        },
      });
      await expect(
        sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
      ).resolves.toMatchObject({
        success: false,
        statusCode: 500,
        message: expect.stringContaining('Response sending to http://example.com: Generic failure'),
      });
    });
  });

  describe('validationManager', () => {
    const url = 'https://example.com';
    const authorizationHeader = 'Bearer token';

    it('should return { data: "OK", status: 200 } for a successful request', async () => {
      (axios.get as jest.Mock).mockResolvedValue({
        status: 200,
        data: 'Success',
      });
      await expect(validationManager(url, authorizationHeader)).resolves.toEqual({
        data: 'OK',
        status: 200,
      });
    });

    it('should throw an error for a 401 Unauthorized response', async () => {
      (axios.get as jest.Mock).mockRejectedValue({
        response: { status: 401, data: 'Unauthorized' },
      });
      await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
        'The user name or password did not match'
      );
    });

    it('should throw an error for a 502 Bad Gateway response', async () => {
      (axios.get as jest.Mock).mockRejectedValue({
        response: { status: 502, data: 'Bad Gateway' },
      });
      await expect(validationManager(url, authorizationHeader)).rejects.toThrow('Bad Gateway');
    });

    it('should throw an error for a 404 Not Found response', async () => {
      (axios.get as jest.Mock).mockRejectedValue({
        response: { status: 404, data: 'Not Found' },
      });
      await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
        'Gateway not found'
      );
    });

    it('should throw an error for a timeout', async () => {
      (axios.get as jest.Mock).mockRejectedValue({ code: 'ECONNABORTED' });
      await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
        'Request timed out'
      );
    });

    it('should throw an error for an unknown error', async () => {
      (axios.get as jest.Mock).mockRejectedValue(new Error('Unknown error'));
      await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
        'Something went wrong while validating the gateway.'
      );
    });

    it('should throw an error for an unexpected status code', async () => {
      (axios.get as jest.Mock).mockResolvedValue({
        status: 418,
        data: 'I’m a teapot',
      });
      await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
        'Something went wrong while validating the gateway.'
      );
    });

    it('should throw an error for unexpected status code', async () => {
      (axios.get as jest.Mock).mockRejectedValue({
        response: { status: 409, data: 'Conflict occurred' },
      });
      await expect(validationManager('http://example.com', 'token')).rejects.toThrow(
        'Conflict occurred'
      );
    });
  });

  describe('ParseAndFindResponse', () => {
    it('should return failed explanations', () => {
      const result = ParseAndFindResponse({
        data: {
          StudioResult: [
            { API: { status: 'Failed', explanation: 'Failed 1' } },
            { API: { status: 'Success' } },
            { API: { status: 'Failed', explanation: 'Failed 2' } },
          ],
        },
      });

      expect(result).toEqual(['Failed 1', 'Failed 2']);
    });

    it('should return empty array for no failures', () => {
      const result = ParseAndFindResponse({
        data: { StudioResult: [{ API: { status: 'Success' } }] },
      });
      expect(result).toEqual([]);
    });

    it('should return empty array for invalid input', () => {
      expect(ParseAndFindResponse(null)).toEqual([]);
      expect(ParseAndFindResponse({ data: {} })).toEqual([]);
    });
  });
});
