import { createRoutingFactory, Spectator } from "@ngneat/spectator/jest";
import { ReturnSummaryItemComponent } from "./return-summary-item.component";
import { MockComponents, MockProvider } from "ng-mocks";
import { ReturnProductInfoComponent } from "@isa/oms/shared/product-info";
import {
  Product,
  ReturnProcess,
  ReturnProcessQuestionKey,
  ReturnProcessService,
} from "@isa/oms/data-access";
import { NgIcon } from "@ng-icons/core";
import { IconButtonComponent } from "@isa/ui/buttons";
import { Location } from "@angular/common";

/**
 * Creates a mock ReturnProcess with default values that can be overridden
 */
function createMockReturnProcess(
  partial: Partial<ReturnProcess>,
): ReturnProcess {
  return {
    id: 1,
    processId: 1,
    productCategory: "Electronics",
    answers: {},
    receiptId: 123,
    receiptItem: {
      id: 321,
      product: {
        name: "Test Product",
      },
    },
    ...partial,
  } as ReturnProcess;
}

describe("ReturnSummaryItemComponent", () => {
  let spectator: Spectator<ReturnSummaryItemComponent>;
  let returnProcessService: jest.Mocked<ReturnProcessService>;

  const createComponent = createRoutingFactory({
    component: ReturnSummaryItemComponent,
    declarations: MockComponents(
      ReturnProductInfoComponent,
      NgIcon,
      IconButtonComponent,
    ),
    providers: [
      MockProvider(ReturnProcessService, {
        getReturnInfo: jest.fn(),
        eligibleForReturn: jest.fn().mockReturnValue({ state: "eligible" }),
      }),
      MockProvider(Location, {
        back: jest.fn(),
      }),
    ],
    shallow: true,
  });

  beforeEach(() => {
    spectator = createComponent({
      props: {
        returnProcess: createMockReturnProcess({}),
      },
    });
    returnProcessService = spectator.inject(ReturnProcessService);
    spectator.detectChanges();
  });

  describe("Component Creation", () => {
    it("should create the component", () => {
      expect(spectator.component).toBeTruthy();
    });
  });

  describe("Return Information Display", () => {
    const mockReturnInfo = {
      itemCondition: "itemCondition",
      returnDetails: { [ReturnProcessQuestionKey.CaseDamaged]: "no" },
      returnReason: "returnReason",
      otherProduct: {
        ean: "ean",
      } as Product,
      comment: "comment",
    };

    beforeEach(() => {
      jest
        .spyOn(returnProcessService, "getReturnInfo")
        .mockReturnValue(mockReturnInfo);
      spectator.setInput("returnProcess", createMockReturnProcess({ id: 2 }));
      spectator.detectChanges();
    });
    it("should provide correct return information array", () => {
      // Arrange
      const expectedInfos = [
        "itemCondition",
        "returnReason",
        "Gehäuse beschädigt: no",
        "Geliefert wurde: ean",
        "comment",
      ];

      // Act
      const actualInfos = spectator.component.returnInfos();

      // Assert
      expect(actualInfos).toEqual(expectedInfos);
      expect(actualInfos.length).toBe(5);
    });
    it("should render return info items with correct content", () => {
      // Arrange
      const expectedInfos = [
        "itemCondition",
        "returnReason",
        "Gehäuse beschädigt: no",
        "Geliefert wurde: ean",
        "comment",
      ];

      // Act
      spectator.detectComponentChanges();
      const listItems = spectator.queryAll(
        '[data-what="list-item"][data-which="return-info"]',
      );

      // Assert
      expect(listItems.length).toBe(expectedInfos.length);
      listItems.forEach((item, index) => {
        expect(item).toHaveText(expectedInfos[index]);
        expect(item).toHaveAttribute("data-info-index", index.toString());
      });
    });

    it("should handle undefined return info gracefully", () => {
      // Arrange
      returnProcessService.getReturnInfo.mockReturnValue(undefined);
      spectator.setInput("returnProcess", createMockReturnProcess({ id: 3 }));
      spectator.detectChanges();

      // Act
      const infos = spectator.component.returnInfos();

      // Assert
      expect(infos).toEqual([]);
      const listItems = spectator.queryAll(
        '[data-what="list-item"][data-which="return-info"]',
      );
      expect(listItems.length).toBe(0);
    });

    describe("returnDetails mapping", () => {
      it("should map multiple returnDetails keys to correct info strings", () => {
        const expected = [
          "itemCondition",
          "returnReason",
          "Gehäuse beschädigt: Ja",
          "Display beschädigt: Nein",
          "Geliefert wurde: ean",
          "comment",
        ];
        // Arrange
        const details = {
          [ReturnProcessQuestionKey.CaseDamaged]: "Ja",
          [ReturnProcessQuestionKey.DisplayDamaged]: "Nein",
        };
        returnProcessService.getReturnInfo.mockReturnValue({
          ...mockReturnInfo,
          returnDetails: details,
        });
        spectator.setInput("returnProcess", createMockReturnProcess({ id: 4 }));
        spectator.detectChanges();

        // Act
        const infos = spectator.component.returnInfos();
        expect(infos).toEqual(expected);
      });

      it("should not include returnDetails if empty", () => {
        // Arrange
        returnProcessService.getReturnInfo.mockReturnValue({
          ...mockReturnInfo,
          returnDetails: {},
        });
        spectator.setInput("returnProcess", createMockReturnProcess({ id: 5 }));
        spectator.detectChanges();

        // Act
        const infos = spectator.component.returnInfos();

        // Assert
        expect(infos.some((info) => info.includes("Gehäuse beschädigt"))).toBe(
          false,
        );
        expect(infos.some((info) => info.includes("Zubehör fehlt"))).toBe(
          false,
        );
      });
    });
  });

  describe("Navigation", () => {
    it("should render edit button with correct attributes", () => {
      // Assert
      const editButton = spectator.query(
        '[data-what="button"][data-which="edit-return-item"]',
      );
      expect(editButton).toExist();
    });

    it("should navigate back when edit button is clicked", () => {
      // Arrange
      const editButton = spectator.query(
        '[data-what="button"][data-which="edit-return-item"]',
      );

      // Act
      if (editButton) {
        spectator.click(editButton);
      }

      // Assert
      expect(spectator.inject(Location).back).toHaveBeenCalled();
    });
  });

  it("should render the product info component", () => {
    const productInfo = spectator.query(ReturnProductInfoComponent);
    expect(productInfo).toExist();
  });

  it("should compute eligibility state as eligible", () => {
    (returnProcessService.eligibleForReturn as jest.Mock).mockReturnValue({
      state: "eligible",
    });
    spectator.detectChanges();
    expect(spectator.component.eligibleForReturn()?.state).toBe("eligible");
  });
});
