import { ProductImageModule, ProductImageService } from '@cdn/product-image';
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
import { OrderItemProcessingStatusValue } from '@swagger/oms';
import { UiSliderModule } from '@ui/slider';
import { first } from 'rxjs/operators';
import { PipesModule } from '../../pipes/pipes.module';
import { SharedGoodsInOutOrderDetailsStore } from '../goods-in-out-order-details.store';
import { SharedGoodsInOutOrderDetailsCoversComponent } from './goods-in-out-order-details-covers.component';

describe('GoodsInOutOrderDetailsCoversComponent', () => {
  let spectator: Spectator<SharedGoodsInOutOrderDetailsCoversComponent>;
  let hostMock: jasmine.SpyObj<SharedGoodsInOutOrderDetailsStore>;
  const orderItemsMock = [
    {
      quantity: 2,
      overallQuantity: 5,
      processingStatus: 128 as OrderItemProcessingStatusValue,
      compartmentCode: 'abc',
      orderItemId: 1,
      orderItemSubsetId: 2,
      product: { ean: '123' },
      readyForPickUp: new Date(2021, 7, 20).toISOString(),
    },
    {
      quantity: 1,
      overallQuantity: 1,
      processingStatus: 16 as OrderItemProcessingStatusValue,
      orderItemId: 3,
      orderItemSubsetId: 4,
      product: { ean: '123' },
    },
    {
      quantity: 1,
      overallQuantity: 1,
      processingStatus: 256 as OrderItemProcessingStatusValue,
      orderItemId: 3,
      orderItemSubsetId: 4,
      product: { ean: '123' },
    },
    {
      quantity: 2,
      overallQuantity: 3,
      processingStatus: 256 as OrderItemProcessingStatusValue,
      orderItemId: 3,
      orderItemSubsetId: 6,
      product: { ean: '123' },
    },
    {
      quantity: 2,
      overallQuantity: 3,
      processingStatus: 128 as OrderItemProcessingStatusValue,
      compartmentCode: 'abc',
      orderItemId: 1,
      orderItemSubsetId: 6,
      product: { ean: '123' },
      readyForPickUp: new Date(2021, 8, 20).toISOString(),
    },
  ];

  const createComponent = createComponentFactory({
    component: SharedGoodsInOutOrderDetailsCoversComponent,
    imports: [PipesModule, ProductImageModule, UiSliderModule],
    providers: [mockProvider(SharedGoodsInOutOrderDetailsStore), mockProvider(ProductImageService)],
  });

  beforeEach(() => {
    spectator = createComponent({ props: { orderItems: orderItemsMock } });
    hostMock = spectator.inject(SharedGoodsInOutOrderDetailsStore);
  });

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

  describe('div .cover', () => {
    it('should render for every group', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(spectator.queryAll('div.cover')).toHaveLength(groupedItems.length);
    });

    it('should render the quantity for every group', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(spectator.query('div.quantity')).toHaveText(groupedItems[0].group);
    });

    describe('button .image', () => {
      it('should render the processingStatus as title', () => {
        expect(spectator.query('button.image')).toHaveAttribute('title', 'bestellt');
      });

      it('should emit coverClick on button image click', async () => {
        let clickedItems;
        spectator.component.coverClick.subscribe((items) => (clickedItems = items));

        const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
        await spectator.click('button.image');
        expect(clickedItems).toEqual(groupedItems[0].items);
      });
    });

    describe('span .compartment-code', () => {
      it('should be visible for all covers where showCoverCompartmentCode returns true', () => {
        expect(spectator.queryAll('span.compartment-code')).toHaveLength(1);
      });

      it('should show the compartmentCode of the first item', async () => {
        const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
        expect(spectator.query('span.compartment-code')).toHaveText(groupedItems[2].items[0].compartmentCode);
      });
    });
  });

  describe('orderItems', () => {
    it('should patch orderItems state when setter is called with different items', () => {
      spyOn(spectator.component, 'patchState');
      spectator.component.orderItems = [];
      expect(spectator.component.patchState).toHaveBeenCalled();
    });

    it('should get current orderItems from store', () => {
      const orderItems = spectator.component.orderItems;
      expect(orderItems).toEqual(orderItemsMock);
    });
  });

  describe('groupedItems$', () => {
    it('should group the orderItemsMock to 3 groups', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(groupedItems).toHaveLength(3);
    });

    it('should be grouped by compartmentCode, ean and orderItemId when processingStatus is 128', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(groupedItems[2].items).toHaveLength(2);
    });

    it('should be grouped by ean and orderItemId when processingStatus is 256', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(groupedItems[1].items).toHaveLength(2);
    });

    it('should be sorted by quantity', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(groupedItems[0].items[0].quantity).toBe(orderItemsMock[1].quantity);
    });

    it('should be sorted by readyForPickUp date', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(groupedItems[2].items[0].readyForPickUp).toBe(orderItemsMock[4].readyForPickUp);
    });

    it('should replace the group name to quantity sum/overallQuantity', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      expect(groupedItems[2].group).toBe('4 / 5');
    });
  });

  describe('ngOnInit()', () => {
    it('should set the host latestCompartmentCode to the latest readyForPickUp compartmentCode', () => {
      expect(hostMock.latestCompartmentCode).toBe(orderItemsMock[4].compartmentCode);
    });

    it('should add a subscription', () => {
      const spy = spyOn(spectator.component['subscriptions'], 'add');
      spectator.component.ngOnInit();
      expect(spy).toHaveBeenCalled();
    });
  });

  describe('ngOnDestroy()', () => {
    it('should unsubscribe the subscriptions', () => {
      const spy = spyOn(spectator.component['subscriptions'], 'unsubscribe');
      spectator.component.ngOnDestroy();
      expect(spy).toHaveBeenCalled();
    });

    it('should set the host latestCompartmentCode to undefined', () => {
      hostMock.latestCompartmentCode = 'test';
      spectator.component.ngOnDestroy();
      expect(hostMock.latestCompartmentCode).toBeUndefined();
    });
  });

  describe('groupByProcessingStatus()', () => {
    it('should group by processingStatus, compartmentCode, ean and orderItemId when processingStatus is 128', () => {
      const result = spectator.component.groupByProcessingStatus(orderItemsMock[0]);
      expect(result).toBe('128_abc_123_1');
    });

    it('should group by processingStatus, ean and orderItemId when processingStatus is 256', () => {
      const result = spectator.component.groupByProcessingStatus(orderItemsMock[2]);
      expect(result).toBe('256_123_3');
    });

    it('should group by ean and orderItemSubsetId when processingStatus is 16', () => {
      const result = spectator.component.groupByProcessingStatus(orderItemsMock[1]);
      expect(result).toBe('4_123');
    });
  });

  describe('sortByQuantity()', () => {
    it('should return -1 when the second group is larger then the first', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      groupedItems[0].group = '2';
      groupedItems[1].group = '3';
      const result = spectator.component.sortByQuantity(groupedItems[0], groupedItems[1]);
      expect(result).toBe(-1);
    });

    it('should return 1 when the second group is lesser then the first', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      groupedItems[0].group = '3';
      groupedItems[1].group = '2';
      const result = spectator.component.sortByQuantity(groupedItems[0], groupedItems[1]);
      expect(result).toBe(1);
    });

    it('should return 0 when both groups are equal', async () => {
      const groupedItems = await spectator.component.groupedItems$.pipe(first()).toPromise();
      groupedItems[0].group = '2';
      groupedItems[1].group = '2';
      const result = spectator.component.sortByQuantity(groupedItems[0], groupedItems[1]);
      expect(result).toBe(0);
    });
  });
});
