import { FormsModule } from '@angular/forms';
import { ProductImageModule, ProductImageService } from '@cdn/product-image';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
import { DomainOmsService } from '@domain/oms';
import { ReorderModalComponent } from '@modal/reorder';
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
import { AvailabilityDTO2, AvailabilityType } from '@swagger/oms';
import { UiCommonModule } from '@ui/common';
import { UiIconModule } from '@ui/icon';
import { UiModalRef } from '@ui/modal';
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
import { UiSelectBulletModule } from '@ui/select-bullet';
import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
import { of } from 'rxjs';
import { SupplierNamePipe } from './supplier-name.pipe';

describe('ReorderModalComponent', () => {
  let spectator: Spectator<ReorderModalComponent>;
  let modalRefMock: jasmine.SpyObj<UiModalRef>;
  let domainAvailabilityServiceMock: jasmine.SpyObj<DomainAvailabilityService>;
  let domainCheckoutServiceMock: jasmine.SpyObj<DomainCheckoutService>;
  let domainOmsServiceMock: jasmine.SpyObj<DomainOmsService>;
  const storeAvailabilitiesMock = [
    { supplier: 'supplier1', status: 1 as AvailabilityType, ssc: '1', sscText: 'TestSSC', supplierId: 1 } as AvailabilityDTO2,
    { supplier: 'supplier2', preferred: 1, status: 2 as AvailabilityType, ssc: '1', sscText: 'TestSSC', supplierId: 2 } as AvailabilityDTO2,
  ];
  const takeAwayAvailabiltyMock = {
    supplier: 'supplier1',
    status: 1 as AvailabilityType,
    ssc: '1',
    sscText: 'TestSSC',
    supplierId: 1,
  } as AvailabilityDTO2;

  const createComponent = createComponentFactory({
    component: ReorderModalComponent,
    imports: [
      UiCommonModule,
      UiSelectBulletModule,
      UiSpinnerModule,
      UiIconModule,
      ProductImageModule,
      FormsModule,
      UiQuantityDropdownModule,
    ],
    declarations: [SupplierNamePipe],
    providers: [
      mockProvider(UiModalRef, { data: { product: {} } }),
      mockProvider(DomainCheckoutService),
      mockProvider(DomainAvailabilityService, {
        getCurrentBranch: jasmine.createSpy().and.returnValue(of({})),
        getPickUpAvailabilities: jasmine.createSpy().and.returnValue(of(storeAvailabilitiesMock)),
        getTakeAwayAvailability: jasmine.createSpy().and.returnValue(of(takeAwayAvailabiltyMock)),
      }),
      mockProvider(DomainOmsService),
      mockProvider(ProductImageService),
    ],
  });

  beforeEach(() => {
    spectator = createComponent();
    modalRefMock = spectator.inject(UiModalRef);
    domainAvailabilityServiceMock = spectator.inject(DomainAvailabilityService);
    domainCheckoutServiceMock = spectator.inject(DomainCheckoutService);
    domainOmsServiceMock = spectator.inject(DomainOmsService);
  });

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

  describe('storeAvailabilities$', () => {
    it('should call domainAvailabilityService.getStoreAvailabilities', async () => {
      const orderItem = { product: { ean: 'Test EAN', catalogProductNumber: '1' }, retailPrice: {}, quantity: 1 };
      spectator.component.patchState({ orderItem });
      await spectator.detectComponentChanges();

      expect(domainAvailabilityServiceMock.getPickUpAvailabilities).toHaveBeenCalled();
    });

    it('should set checkedAvailability to the preferred availability', async () => {
      const orderItem = { product: { ean: 'Test EAN', catalogProductNumber: '1' }, retailPrice: {}, quantity: 3 };

      spectator.component.patchState({ orderItem });
      await spectator.detectComponentChanges();

      expect(spectator.component.checkedSupplier).toBe(storeAvailabilitiesMock.find((a) => a.preferred === 1)?.supplier);
    });
  });

  describe('button .close-btn', () => {
    it('should call uiModalRef.close() on click', () => {
      spectator.click('button.close-btn');
      expect(modalRefMock.close).toHaveBeenCalled();
    });
  });

  describe('details', () => {
    it('should render product-name', async () => {
      const orderItem = { product: { name: 'Test Product' } };
      spectator.component.patchState({ orderItem });
      spectator.detectComponentChanges();
      expect(spectator.query('.details .product-name')).toHaveText(orderItem.product.name);
    });

    it('should render product ean', () => {
      const orderItem = { product: { ean: 'Test EAN' } };
      spectator.component.patchState({ orderItem });
      spectator.detectComponentChanges();
      expect(spectator.query('.details .product-ean')).toHaveText(orderItem.product.ean);
    });

    describe('div .format', () => {
      it('should not render when format is not set', () => {
        const orderItem = { product: { formatDetail: 'TestFormatDetail' } };
        spectator.component.patchState({ orderItem });
        spectator.detectComponentChanges();

        expect(spectator.query('div .details .product-format')).not.toBeVisible();
      });

      it('should render format-icon when format and formatDetail is set', () => {
        const orderItem = { product: { format: 'TestFormat', formatDetail: 'TestFormatDetail' } };
        spectator.component.patchState({ orderItem });
        spectator.detectComponentChanges();

        const src = '/assets/images/Icon_' + orderItem.product?.format + '.svg';
        expect(spectator.query('div .details .format-icon')).toHaveAttribute('src', src);
      });

      it('should render format-text when format and formatDetail is set', () => {
        const orderItem = { product: { format: 'TestFormat', formatDetail: 'TestFormatDetail' } };
        spectator.component.patchState({ orderItem });
        spectator.detectComponentChanges();

        expect(spectator.query('div .details .product-format')).toHaveText(orderItem.product.formatDetail);
      });
    });
  });

  describe('ui-spinner', () => {
    it('should not be visible when storeAvailabilities$ are set', () => {
      expect(spectator.query('ui-spinner')).not.toBeVisible();
    });

    it('should be visible when availabilities$ are undefined', async () => {
      Object.defineProperty(spectator.component, 'availabilities$', { value: undefined });
      await spectator.detectComponentChanges();
      expect(spectator.query('ui-spinner')).toBeVisible();
    });
  });

  describe('button .cta-not-available', () => {
    it('should call notAvailable() on click', () => {
      spectator.component.notAvailable = jasmine.createSpy();
      spectator.click('button.cta-not-available');
      expect(spectator.component.notAvailable).toHaveBeenCalled();
    });
  });

  describe('button .cta-reorder', () => {
    it('should call reorder() on click', () => {
      spectator.component.reorder = jasmine.createSpy();
      spectator.click('button.cta-reorder');
      expect(spectator.component.reorder).toHaveBeenCalled();
    });
  });

  describe('checked()', () => {
    it('should set the checkedAvailability when event is passed', () => {
      const availability = {} as AvailabilityDTO2;
      spectator.component.checked('test', availability);
      expect(spectator.component.checkedAvailability).toEqual(availability);
    });

    it('should set the checkedAvailability to undefined when no event is passed', () => {
      spectator.component.checked(undefined, undefined);
      expect(spectator.component.checkedAvailability).toBeUndefined();
    });
  });

  describe('reorder()', () => {
    it('should not call domainCheckoutService.reorder() when no checkedAvailability is set', () => {
      domainCheckoutServiceMock.reorder = jasmine.createSpy().and.returnValue(of({}));
      spectator.component.checkedAvailability = undefined;
      spectator.component.reorder();
      expect(domainCheckoutServiceMock.reorder).not.toHaveBeenCalled();
    });

    it('should call domainCheckoutService.reorder() when checkedAvailability is set', async () => {
      domainCheckoutServiceMock.reorder = jasmine.createSpy().and.returnValue(of({}));

      const orderItem = { orderId: 1, orderItemId: 2, orderItemSubsetId: 3, product: {}, quantity: 2 };
      spectator.component.patchState({ orderItem });
      await spectator.detectComponentChanges();

      spectator.component.checkedAvailability = storeAvailabilitiesMock[0];
      await spectator.component.reorder();

      expect(domainCheckoutServiceMock.reorder).toHaveBeenCalledWith(
        orderItem.orderId,
        orderItem.orderItemId,
        orderItem.orderItemSubsetId,
        {
          quantity: orderItem.quantity,
          availability: {
            availabilityType: storeAvailabilitiesMock[0].status,
            ssc: storeAvailabilitiesMock[0].ssc,
            sscText: storeAvailabilitiesMock[0].sscText,
            supplier: {
              id: storeAvailabilitiesMock[0].supplierId,
            },
            isPrebooked: storeAvailabilitiesMock[0].isPrebooked,
            estimatedShippingDate: storeAvailabilitiesMock[0].at,
            price: storeAvailabilitiesMock[0].price,
            inStock: storeAvailabilitiesMock[0].qty,
          },
        }
      );
    });

    it('should call modalRef.close()', async () => {
      domainCheckoutServiceMock.reorder = jasmine.createSpy().and.returnValue(of({}));

      const orderItem = { orderId: 1, orderItemId: 2, orderItemSubsetId: 3, product: {}, quantity: 2 };
      spectator.component.patchState({ orderItem });
      await spectator.detectComponentChanges();

      spectator.component.checkedAvailability = storeAvailabilitiesMock[0];
      await spectator.component.reorder();

      expect(modalRefMock.close).toHaveBeenCalled();
    });
  });

  describe('notAvailable()', () => {
    it('should call domainOmsService.changeOrderStatus()', async () => {
      domainOmsServiceMock.changeOrderStatus = jasmine.createSpy().and.returnValue(of({}));

      const orderItem = { orderId: 1, orderItemId: 2, orderItemSubsetId: 3, product: {} };
      spectator.component.patchState({ orderItem });
      await spectator.detectComponentChanges();

      await spectator.component.notAvailable();
      expect(domainOmsServiceMock.changeOrderStatus).toHaveBeenCalledWith(
        orderItem.orderId,
        orderItem.orderItemId,
        orderItem.orderItemSubsetId,
        {
          processingStatus: 4096,
        }
      );
    });

    it('should call modalRef.close()', async () => {
      domainOmsServiceMock.changeOrderStatus = jasmine.createSpy().and.returnValue(of({}));

      await spectator.component.notAvailable();
      expect(modalRefMock.close).toHaveBeenCalled();
    });
  });
});
