/**
 * @jest-environment jsdom
 */
import CfCore from "../../../core/CfCore";
import ECommerce from "..";

import {
  ECommerceTypes,
  CancelCheckoutProperties,
  CancelType,
} from "../typings";

import { ICatalogRepository } from "../../../core/repositories/catalog/CatalogRepository";
import { ContentBlock } from "../../Navigation/typings";

let windowSpy;
let mockSender;
let mockImpressionManager = {
  on: () => {},
};

let mockCatalogRepository: ICatalogRepository = {
  injectBlood: (itemId: string, properties: any) => new Promise((r) => r()),
  injectDrug: (itemId: string, properties: any) => new Promise((r) => r()),
  injectMedicalEquipment: (itemId: string, properties: any) =>
    new Promise((r) => r()),
  injectOxygen: (itemId: string, properties: any) => new Promise((r) => r()),
  injectMedia: (properties: any) => {},
  injectFacility: (itemId: string, properties: any) => new Promise((r) => r()),
  injectGrocery: (itemId: string, properties: any) => new Promise((r) => r()),
  injectUser: (properties: any) => new Promise((r) => r()),
  injectSurvey: (subjectId: string, properties: any) => new Promise((r) => r()),
  injectReward: (subjectId: string, properties: any) => new Promise((r) => r()),
  injectHCW: (properties: any) => new Promise((r) => r()),
  injectPatient: (properties: any) => new Promise((r) => r()),
  injectSite: (properties: any) => new Promise((r) => r()),
};

const device_id = "asdf";
describe("ECommerce", () => {
  beforeAll(() => {
    ECommerce.init(mockCatalogRepository);
  });

  beforeEach(() => {
    mockSender = {
      add() {
        return false;
      },
      sendException() {
        return false;
      },
    };
    windowSpy = jest.spyOn(window, "window", "get");
  });

  afterEach(() => {
    // to remove the singleton instance
    (CfCore as any).instance = null;
  });

  it('Should deliver a "CancelCheckout" event', async () => {
    const senderSpy = jest.spyOn(mockSender, "add");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: "cancel_123",
      type: CancelType.Cart,
      items: ["item1", "item2"],
      reason: "User requested cancellation",
    };

    await ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true);

    expect(senderSpy).toHaveBeenCalledWith(
      expect.stringMatching(device_id),
      expect.stringMatching(device_id),
      expect.objectContaining({
        name: ECommerceTypes.CancelCheckout,
        ctx: { ...event },
      }),
      true,
      expect.anything()
    );
  });

  it("Should throw exception when cancel_type_id is missing", async () => {
    const addSpy = jest.spyOn(mockSender, "add");
    const sendExceptionSpy = jest.spyOn(mockSender, "sendException");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: undefined as any, // Missing cancel_type_id
      type: CancelType.Cart,
      items: ["item1", "item2"],
      reason: "User requested cancellation",
    };

    await expect(
      ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true)
    ).rejects.toThrow();

    expect(addSpy).not.toHaveBeenCalled();
    expect(sendExceptionSpy).toHaveBeenCalled();
  });

  it("Should throw exception when type is missing", async () => {
    const addSpy = jest.spyOn(mockSender, "add");
    const sendExceptionSpy = jest.spyOn(mockSender, "sendException");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: "cancel_123",
      type: undefined as any, // Missing type
      items: ["item1", "item2"],
      reason: "User requested cancellation",
    };

    await expect(
      ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true)
    ).rejects.toThrow();

    expect(addSpy).not.toHaveBeenCalled();
    expect(sendExceptionSpy).toHaveBeenCalled();
  });

  it("Should throw exception when items is missing", async () => {
    const addSpy = jest.spyOn(mockSender, "add");
    const sendExceptionSpy = jest.spyOn(mockSender, "sendException");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: "cancel_123",
      type: CancelType.Cart,
      items: undefined as any, // Missing items
      reason: "User requested cancellation",
    };

    await expect(
      ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true)
    ).rejects.toThrow();

    expect(addSpy).not.toHaveBeenCalled();
    expect(sendExceptionSpy).toHaveBeenCalled();
  });

  it("Should throw exception when reason is missing", async () => {
    const addSpy = jest.spyOn(mockSender, "add");
    const sendExceptionSpy = jest.spyOn(mockSender, "sendException");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: "cancel_123",
      type: CancelType.Cart,
      items: ["item1", "item2"],
      reason: undefined as any, // Missing reason
    };

    await expect(
      ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true)
    ).rejects.toThrow();

    expect(addSpy).not.toHaveBeenCalled();
    expect(sendExceptionSpy).toHaveBeenCalled();
  });

  it("Should throw exception when items array is empty", async () => {
    const addSpy = jest.spyOn(mockSender, "add");
    const sendExceptionSpy = jest.spyOn(mockSender, "sendException");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: "cancel_123",
      type: CancelType.Cart,
      items: [], // Empty items array
      reason: "User requested cancellation",
    };

    await expect(
      ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true)
    ).rejects.toThrow();

    expect(addSpy).not.toHaveBeenCalled();
    expect(sendExceptionSpy).toHaveBeenCalled();
  });

  it("Should throw exception when items array contains repeated ids", async () => {
    const addSpy = jest.spyOn(mockSender, "add");
    const sendExceptionSpy = jest.spyOn(mockSender, "sendException");

    const cfCore = CfCore.createInstance(
      mockSender as any,
      mockImpressionManager as any,
      false,
      ContentBlock.Core,
      device_id,
      false
    );

    const event: CancelCheckoutProperties = {
      cancel_type_id: "cancel_123",
      type: CancelType.Cart,
      items: ["item1", "item2", "item1"], // Repeated item1
      reason: "User requested cancellation",
    };

    await expect(
      ECommerce.logIngestEvent(ECommerceTypes.CancelCheckout, event, true)
    ).rejects.toThrow();

    expect(addSpy).not.toHaveBeenCalled();
    expect(sendExceptionSpy).toHaveBeenCalled();
  });
});
