import { createComponentFactory, Spectator } from "@ngneat/spectator/jest";
import { MockDirective } from "ng-mocks";

import {
  ReceiptItem,
  ReturnDetailsService,
  ReturnDetailsStore,
} from "@isa/oms/data-access";

import { ProductImageDirective } from "@isa/shared/product-image";
import { ReturnDetailsOrderGroupItemControlsComponent } from "../return-details-order-group-item-controls/return-details-order-group-item-controls.component";
import { CheckboxComponent } from "@isa/ui/input-controls";
import { signal } from "@angular/core";

// Helper function to create mock ReceiptItem data
const createMockItem = (
  ean: string,
  canReturn: boolean,
  name = "Test Product",
  category = "BOOK", // Add default category that's not 'unknown'
  availableQuantity = 2,
  selectedQuantity = 1,
): ReceiptItem =>
  ({
    id: 123,
    receiptNumber: "R-123456",
    quantity: { quantity: availableQuantity },
    price: {
      value: { value: 19.99, currency: "EUR" },
      vat: { inPercent: 19 },
    },
    product: {
      ean: ean,
      name: name,
      contributors: "Test Author",
      format: "HC",
      formatDetail: "Hardcover",
      manufacturer: "Test Publisher",
      publicationDate: "2024-01-01T00:00:00Z",
      catalogProductNumber: "1234567890",
      volume: "1",
    },
    actions: [{ key: "canReturn", value: String(canReturn) }],
    features: { category: category },
  }) as ReceiptItem;

describe("ReturnDetailsOrderGroupItemControlsComponent", () => {
  let spectator: Spectator<ReturnDetailsOrderGroupItemControlsComponent>;
  const mockItemSelectable = createMockItem("1234567890123", true);

  const mockIsSelectable = signal<boolean>(true);
  const mockGetItemSelectted = signal<boolean>(false);
  const mockCanReturnResource = {
    isLoading: signal<boolean>(true),
  };

  // Mocks for availableQuantityMap and selectedQuantityMap
  const mockAvailableQuantityMap = { [mockItemSelectable.id]: 2 };
  const mockSelectedQuantityMap = { [mockItemSelectable.id]: 1 };
  const mockItemCategoryMap = { [mockItemSelectable.id]: "BOOK" };

  function resetMocks() {
    mockIsSelectable.set(true);
    mockGetItemSelectted.set(false);
    mockCanReturnResource.isLoading.set(true);
  }

  const createComponent = createComponentFactory({
    component: ReturnDetailsOrderGroupItemControlsComponent,
    mocks: [ReturnDetailsService],
    providers: [
      {
        provide: ReturnDetailsStore,
        useValue: {
          isSelectable: jest.fn(() => mockIsSelectable),
          getItemSelected: jest.fn(() => mockGetItemSelectted),
          canReturnResource: jest.fn(() => mockCanReturnResource),
          availableQuantityMap: jest.fn(() => mockAvailableQuantityMap),
          selectedQuantityMap: jest.fn(() => mockSelectedQuantityMap),
          itemCategoryMap: jest.fn(() => mockItemCategoryMap),
          setProductCategory: jest.fn(),
          setQuantity: jest.fn(),
          addSelectedItems: jest.fn(),
          removeSelectedItems: jest.fn(),
        },
      },
    ],
    overrideComponents: [
      [
        ReturnDetailsOrderGroupItemControlsComponent,
        {
          remove: { imports: [ProductImageDirective] },
          add: {
            imports: [MockDirective(ProductImageDirective)],
          },
        },
      ],
    ],
    detectChanges: false,
  });

  beforeEach(() => {
    spectator = createComponent({
      props: {
        item: mockItemSelectable,
      },
    });
  });

  afterEach(() => {
    resetMocks();
  });

  it("should create", () => {
    spectator.detectChanges();
    expect(spectator.component).toBeTruthy();
  });

  it("should display the checkbox when item is selectable and not loading", () => {
    mockCanReturnResource.isLoading.set(false);
    mockIsSelectable.set(true);
    spectator.detectChanges();
    expect(spectator.component.selectable()).toBe(true);
    expect(spectator.query(CheckboxComponent)).toBeTruthy();
    expect(
      spectator.query(`input[data-what="return-item-checkbox"]`),
    ).toExist();
  });

  it("should NOT display the checkbox when item is not selectable", () => {
    mockIsSelectable.set(false);
    mockCanReturnResource.isLoading.set(false);
    spectator.detectChanges();
    expect(spectator.component.selectable()).toBe(false);
    expect(
      spectator.query(`input[data-what="return-item-checkbox"]`),
    ).not.toExist();
    expect(spectator.query(CheckboxComponent)).toBeFalsy();
  });

  it("should show spinner when canReturnResource is loading", () => {
    mockCanReturnResource.isLoading.set(true);
    spectator.detectChanges();
    expect(
      spectator.query('ui-icon-button[data-what="load-spinner"]'),
    ).toExist();
  });

  it("should render correct quantity dropdown values", () => {
    spectator.detectChanges();
    expect(spectator.component.quantityDropdownValues()).toEqual([1, 2]);
  });

  it("should call setQuantity when dropdown value changes", () => {
    const store = spectator.inject(ReturnDetailsStore);
    const spy = jest.spyOn(store, "setQuantity");
    spectator.detectChanges();
    // Simulate dropdown value change
    spectator.component.setQuantity(2);
    expect(spy).toHaveBeenCalledWith(mockItemSelectable.id, 2);
  });

  it("should call setProductCategory when product category changes", () => {
    const store = spectator.inject(ReturnDetailsStore);
    const spy = jest.spyOn(store, "setProductCategory");
    spectator.detectChanges();
    spectator.component.setProductCategory("Buch/Kalender");
    expect(spy).toHaveBeenCalledWith(mockItemSelectable.id, "Buch/Kalender");
  });

  it("should call addSelectedItems when setSelected(true) is called", () => {
    const store = spectator.inject(ReturnDetailsStore);
    const spy = jest.spyOn(store, "addSelectedItems");
    spectator.detectChanges();
    spectator.component.setSelected(true);
    expect(spy).toHaveBeenCalledWith([mockItemSelectable.id]);
  });

  it("should call removeSelectedItems when setSelected(false) is called", () => {
    const store = spectator.inject(ReturnDetailsStore);
    const spy = jest.spyOn(store, "removeSelectedItems");
    spectator.detectChanges();
    spectator.component.setSelected(false);
    expect(spy).toHaveBeenCalledWith([mockItemSelectable.id]);
  });

  it("should be false when no canReturn action is present", () => {
    const item = { ...createMockItem("0001", true), actions: [] };
    spectator.setInput("item", item as any);
    spectator.detectChanges();
    expect(spectator.component.canReturnReceiptItem()).toBe(false);
  });

  it("should be false when canReturn action has falsy value", () => {
    const item = createMockItem("0001", false);
    spectator.setInput("item", item);
    spectator.detectChanges();
    expect(spectator.component.canReturnReceiptItem()).toBe(false);
  });

  it("should display correct selected quantity", () => {
    spectator.detectChanges();
    expect(spectator.component.selectedQuantity()).toBe(1);
  });

  it("should display correct product category", () => {
    spectator.detectChanges();
    expect(spectator.component.productCategory()).toBe("BOOK");
  });
});
