import { returnReceiptValuesMapping } from "./return-receipt-values-mapping.helper";
import { PropertyNullOrUndefinedError } from "@isa/common/data-access";
import { getReturnProcessQuestions } from "./get-return-process-questions.helper";
import { getReturnInfo } from "./get-return-info.helper";
import { serializeReturnDetails } from "./return-details-mapping.helper";

// Mock dependencies
jest.mock("./get-return-process-questions.helper", () => ({
  getReturnProcessQuestions: jest.fn(),
}));
jest.mock("./get-return-info.helper", () => ({
  getReturnInfo: jest.fn(),
}));
jest.mock("./return-details-mapping.helper", () => ({
  serializeReturnDetails: jest.fn(),
}));

describe("returnReceiptValuesMapping", () => {
  const processMock: any = {
    receiptItem: {
      id: "item-1",
    },
    quantity: 2, // <-- Add this
    productCategory: "shoes", // <-- Add this
    answers: { foo: "bar" },
  };

  const questionsMock = [{ id: "q1" }];
  const returnInfoMock = {
    comment: "Test comment",
    itemCondition: "NEW",
    otherProduct: "Other",
    returnDetails: { detail: "details" },
    returnReason: "Damaged",
  };
  const serializedDetails = { detail: "serialized" };

  beforeEach(() => {
    jest.clearAllMocks();
    (getReturnProcessQuestions as jest.Mock).mockReturnValue(questionsMock);
    (getReturnInfo as jest.Mock).mockReturnValue(returnInfoMock);
    (serializeReturnDetails as jest.Mock).mockReturnValue(serializedDetails);
  });

  it("should map values correctly when all dependencies return valid data", () => {
    // Act
    const result = returnReceiptValuesMapping(processMock);

    // Assert
    expect(result).toEqual({
      quantity: 2,
      comment: "Test comment",
      itemCondition: "NEW",
      otherProduct: "Other",
      returnDetails: serializedDetails,
      returnReason: "Damaged",
      category: "shoes",
      receiptItem: { id: "item-1" },
    });
  });

  it("should throw PropertyNullOrUndefinedError if questions is undefined", () => {
    // Arrange
    (getReturnProcessQuestions as jest.Mock).mockReturnValue(undefined);

    // Act & Assert
    expect(() => returnReceiptValuesMapping(processMock)).toThrow(
      PropertyNullOrUndefinedError,
    );
    expect(() => returnReceiptValuesMapping(processMock)).toThrow("questions");
  });

  it("should throw PropertyNullOrUndefinedError if returnInfo is undefined", () => {
    // Arrange
    (getReturnInfo as jest.Mock).mockReturnValue(undefined);

    // Act & Assert
    expect(() => returnReceiptValuesMapping(processMock)).toThrow(
      PropertyNullOrUndefinedError,
    );
    expect(() => returnReceiptValuesMapping(processMock)).toThrow("returnInfo");
  });

  it("should handle missing receiptItem gracefully (may throw)", () => {
    // Arrange
    const processNoReceiptItem = { ...processMock, receiptItem: undefined };

    // Act & Assert
    expect(() => returnReceiptValuesMapping(processNoReceiptItem)).toThrow();
  });

  // Additional tests for edge cases and error scenarios

  it("should return correct quantity when process.quantity is 0", () => {
    const processZeroQuantity = { ...processMock, quantity: 0 };
    const result = returnReceiptValuesMapping(processZeroQuantity);
    expect(result?.quantity).toBe(0);
  });

  it("should propagate the correct receiptItem id", () => {
    const result = returnReceiptValuesMapping(processMock);
    expect(result?.receiptItem).toEqual({ id: "item-1" });
  });

  it("should throw if process is null", () => {
    expect(() => returnReceiptValuesMapping(null as any)).toThrow();
  });

  it("should throw if process is undefined", () => {
    expect(() => returnReceiptValuesMapping(undefined as any)).toThrow();
  });

  it("should call serializeReturnDetails with undefined if returnDetails is missing", () => {
    // Arrange
    const returnInfoNoDetails = { ...returnInfoMock, returnDetails: undefined };
    (getReturnInfo as jest.Mock).mockReturnValue(returnInfoNoDetails);

    // Act
    returnReceiptValuesMapping(processMock);

    // Assert
    expect(serializeReturnDetails).toHaveBeenCalledWith(undefined);
  });

  it("should return undefined if process.quantity is undefined", () => {
    const processNoQuantity = { ...processMock };
    delete processNoQuantity.quantity;
    // Should not throw, but quantity will be undefined in result
    const result = returnReceiptValuesMapping(processNoQuantity);
    expect(result?.quantity).toBeUndefined();
  });
});
