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

import { ECommerceTypes, DeliveryProperties, DeliveryAction } from "../typings";
import { ContentBlock } from "../../Navigation/typings";

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

const device_id = "asdf";
describe("ECommerce", () => {
  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 "Delivery" 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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: "1937-01-01T12:00:27.87+00:20",
      delivery_coordinates: {
        lat: 1,
        lon: 1,
      },
    };

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

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

  it("Should throw exception when delivery_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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: undefined as any, // Missing delivery_id
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: "2024-01-15T14:30:00Z",
    };

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

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

  it("Should throw exception when order_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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: undefined as any, // Missing order_id
      is_urgent: true,
      est_delivery_ts: "2024-01-15T14:30:00Z",
    };

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

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

  it("Should throw exception when is_urgent 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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: "111",
      is_urgent: undefined as any, // Missing is_urgent
      est_delivery_ts: "2024-01-15T14:30:00Z",
    };

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

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

  it("Should throw exception when action 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: DeliveryProperties = {
      action: undefined as any, // Missing action
      delivery_id: "111",
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: "2024-01-15T14:30:00Z",
    };

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

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

  it("Should throw exception when est_delivery_ts 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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: undefined as any, // Missing est_delivery_ts
    };

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

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

  it("Should throw exception when delivery_coordinates has zero values", 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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: "2024-01-15T14:30:00Z",
      delivery_coordinates: [0, 0],
    };

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

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

  it("Should throw exception when dispatch_coordinates has zero values", 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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: "2024-01-15T14:30:00Z",
      dispatch_coordinates: [0, 0],
    };

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

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

  it("Should throw exception when est_delivery_ts has invalid date format", 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: DeliveryProperties = {
      action: DeliveryAction.Dispatch,
      delivery_id: "111",
      order_id: "111",
      is_urgent: true,
      est_delivery_ts: "15/01/2024 14:30", // Invalid: not ISO8601 format
    };

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

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