/**
 * Copyright IBM Corp. 2024, 2025
 */
import { processDeployment, validateGateways } from '../../src/index.js';
import fs from 'fs';
import path from 'path';
import * as GatewayService from '../../src/service/gateway-service.js';
import { AppConstants } from '../../src/constants/app-constants.js';
import { IpcError, Logger } from '@apic/studio-shared';

// Mock the Logger from studio-shared
jest.mock('@apic/studio-shared', () => ({
  Logger: {
    info: jest.fn(),
    debug: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    log: jest.fn(),
  },
  IpcError: class IpcError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'IpcError';
    }
  },
}));

jest.mock('@apic/wmgw-smith-sdk', () => ({
  WMGWRuntimeSDK: jest.fn().mockImplementation(() => ({
    transformer: {
      transform: jest.fn().mockResolvedValue(Buffer.from('transformed')),
    },
    inventory: jest.fn(),
  })),
}));

jest.mock('@apic/wmgw-smith-transformer', () => ({
  createWmgwOrchestrator: jest.fn().mockImplementation(() => ({
    transform: jest.fn().mockResolvedValue(Buffer.from('transformed')),
  })),
}));

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

  it('should return success response on deployment', async () => {
    const gatewaysJson = {
      gateways: [
        {
          gatewayURL: 'http://example.com',
          gatewayUser: 'user',
          gatewaySecret: '123456',
          is_mcsp_enabled: false,
        },
      ],
      overwrite: 'apis,alias',
      skip: 'policies',
    };
    const zipBuffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-asset.zip'));

    jest.spyOn(GatewayService, 'sendToGateway').mockResolvedValue({
      success: true,
      statusCode: 200,
      message: 'Api deployment Successful',
      data: 'success',
      errors: [],
    });

    const result = await processDeployment(gatewaysJson, zipBuffer);

    expect(result).toStrictEqual([
      {
        success: true,
        statusCode: 200,
        message: 'Api deployment Successful',
        data: 'success',
        errors: [],
      },
    ]);

    expect(Logger.info).toHaveBeenCalledWith('Starting deployment process', { gatewayCount: 1 });
    expect(Logger.debug).toHaveBeenCalledWith(
      'Deploying to gateway',
      expect.objectContaining({ gatewayURL: 'http://example.com' })
    );
    expect(Logger.debug).toHaveBeenCalledWith('Gateway deployment successful', {
      response: 'success',
    });
    expect(Logger.info).toHaveBeenCalledWith('Deployment process completed.');
    expect(Logger.error).not.toHaveBeenCalled();
    expect(Logger.warn).not.toHaveBeenCalled();
  });

  it('should return multiple success responses on multi deployment', async () => {
    const gatewaysJson = {
      gateways: [
        {
          gatewayURL: 'http://example.com',
          gatewayUser: 'user',
          gatewaySecret: '123456',
          is_mcsp_enabled: false,
        },
        {
          gatewayURL: 'http://example2.com',
          gatewayUser: 'user2',
          gatewaySecret: 'abcdef',
          is_mcsp_enabled: true,
        },
      ],
      overwrite: 'apis,alias',
      skip: 'policies',
    };
    const zipBuffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-asset.zip'));

    jest.spyOn(GatewayService, 'sendToGateway').mockResolvedValue({
      success: true,
      statusCode: 200,
      message: 'Api deployment Successful',
      data: 'success',
      errors: [],
    });

    const result = await processDeployment(gatewaysJson, zipBuffer);

    expect(result.length).toBe(2);
    expect(result[0].success).toBeTruthy();
    expect(result[1].success).toBeTruthy();

    expect(Logger.info).toHaveBeenCalledWith('Starting deployment process', { gatewayCount: 2 });
    expect(Logger.debug).toHaveBeenCalledWith(
      'Deploying to gateway',
      expect.objectContaining({ gatewayURL: 'http://example.com' })
    );
    expect(Logger.debug).toHaveBeenCalledWith(
      'Deploying to gateway',
      expect.objectContaining({ gatewayURL: 'http://example2.com' })
    );
    expect(Logger.info).toHaveBeenCalledWith('Deployment process completed.');
    expect(Logger.error).not.toHaveBeenCalled();
    expect(Logger.warn).not.toHaveBeenCalled();
  });

  it('should return warning on deployment with no gateways provided', async () => {
    const gatewaysJson = {
      gateways: [],
      overwrite: 'apis,alias',
      skip: 'policies',
    };
    const zipBuffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-asset.zip'));

    const result = await processDeployment(gatewaysJson, zipBuffer);

    expect(result).toStrictEqual([]);

    expect(Logger.warn).toHaveBeenCalledWith('No gateways to deploy.');
    expect(Logger.info).toHaveBeenCalledWith('Deployment process completed.');
    expect(Logger.error).not.toHaveBeenCalled();
    expect(Logger.debug).not.toHaveBeenCalled();
  });

  it('should return error response on deployment error', async () => {
    const gatewaysJson = {
      gateways: [
        {
          gatewayURL: 'http://example.com',
          gatewayUser: 'user',
          gatewaySecret: '123456',
          is_mcsp_enabled: false,
        },
      ],
      overwrite: 'apis,alias',
      skip: 'policies',
    };
    const zipBuffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-asset.zip'));

    jest.spyOn(GatewayService, 'sendToGateway').mockImplementation(() => {
      throw new Error('Gateway not found');
    });

    const result = await processDeployment(gatewaysJson, zipBuffer);

    expect(result).toStrictEqual([
      {
        success: false,
        statusCode: 400,
        message: 'Error sending to http://example.com: Gateway not found',
        data: null,
        errors: ['Error sending to http://example.com: Gateway not found'],
      },
    ]);

    expect(Logger.error).toHaveBeenCalledWith(
      'Deployment failed',
      expect.any(Error),
      expect.objectContaining({ context: 'deploying to', gatewayURL: 'http://example.com' })
    );
    expect(Logger.warn).not.toHaveBeenCalled();
  });

  it('should return error response on deployment error due to promise rejection', async () => {
    const gatewaysJson = {
      gateways: [
        {
          gatewayURL: 'http://example.com',
          gatewayUser: 'user',
          gatewaySecret: '123456',
          is_mcsp_enabled: false,
        },
      ],
      overwrite: 'apis,alias',
      skip: 'policies',
    };
    const zipBuffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-asset.zip'));

    jest.spyOn(GatewayService, 'sendToGateway').mockRejectedValue({ some: 'random object' });

    const result = await processDeployment(gatewaysJson, zipBuffer);

    expect(result).toStrictEqual([
      {
        success: false,
        statusCode: 400,
        message: 'Unknown error sending to http://example.com',
        data: null,
        errors: ['Unknown error sending to http://example.com'],
      },
    ]);

    expect(Logger.error).toHaveBeenCalledWith(
      'Unknown deployment error',
      expect.any(Error),
      expect.objectContaining({ gatewayURL: 'http://example.com' })
    );
    expect(Logger.info).toHaveBeenCalledWith('Deployment process completed.');
    expect(Logger.warn).not.toHaveBeenCalled();
  });

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

    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should call validationManager and return success', async () => {
      jest
        .spyOn(GatewayService, 'validationManager')
        .mockResolvedValue({ data: 'OK', status: 200 });

      const result = await validateGateways(url, authHeader);

      expect(result).toEqual({ data: 'OK', status: 200 });
      expect(GatewayService.validationManager).toHaveBeenCalledWith(
        `${url}${AppConstants.GATEWAY_VALIDATION_URL}`,
        authHeader
      );
    });

    it('should throw error if URL is missing', async () => {
      await expect(validateGateways('', authHeader)).rejects.toThrow(IpcError);
    });

    it('should throw error if authHeader is missing', async () => {
      await expect(validateGateways(url, '')).rejects.toThrow(IpcError);
    });
  });
});
