import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ProductImageModule, ProductImageService } from '@cdn/product-image';
import { DomainOmsService } from '@domain/oms';
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
import { OrderItemProcessingStatusValue } from '@swagger/oms';
import { UiIconModule } from '@ui/icon';
import { UiModalService } from '@ui/modal';
import { PipesModule } from 'apps/shared/goods-in-out/src/lib/pipes/pipes.module';
import { Subject } from 'rxjs';
import { GoodsInListReorderModalComponent } from '../goods-in-list-reorder-modal/goods-in-list-reorder-modal.component';
import { GoodsInListItemComponent } from './goods-in-list-item.component';

describe('GoodsInListItemComponent', () => {
  let spectator: Spectator<GoodsInListItemComponent>;
  let uiModalServiceMock: jasmine.SpyObj<UiModalService>;
  let domainOmsServiceMock: jasmine.SpyObj<DomainOmsService>;
  let orderItemMock = { product: {}, supplierId: 1 };

  const createComponent = createComponentFactory({
    component: GoodsInListItemComponent,
    imports: [UiIconModule, ProductImageModule, PipesModule, ReactiveFormsModule, FormsModule],
    providers: [mockProvider(DomainOmsService), mockProvider(UiModalService), mockProvider(ProductImageService)],
    componentMocks: [ProductImageService],
  });

  beforeEach(() => {
    spectator = createComponent({ props: { item: orderItemMock } });
    domainOmsServiceMock = spectator.inject(DomainOmsService);
    uiModalServiceMock = spectator.inject(UiModalService);
  });

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

  describe('div .header', () => {
    it('should render organisation when it is set', () => {
      const item = { ...orderItemMock, organisation: 'Test GmbH' };
      spectator.setInput({ item });
      expect(spectator.query('div .header')).toHaveText(item.organisation);
    });

    it('should render firstname and lastname when it is set', () => {
      const item = { ...orderItemMock, firstName: 'TestFirstName', lastName: 'TestLastName' };
      spectator.setInput({ item });
      expect(spectator.query('div .header')).toHaveText(`${item.firstName} ${item.lastName}`);
    });

    it('should render organisation, firstName and lastName separated when all is set', () => {
      const item = { ...orderItemMock, firstName: 'TestFirstName', lastName: 'TestLastName', organisation: 'Test GmbH' };
      spectator.setInput({ item });
      expect(spectator.query('div .header')).toHaveText(`${item.organisation} - ${item.firstName} ${item.lastName}`);
    });
  });

  describe('div .header .ssc', () => {
    it('should render ssc and sscText separated when editSsc is false and both are set', () => {
      const item = { ...orderItemMock, ssc: '000', sscText: 'SSC Text' };
      spectator.setInput({ editSsc: false, item });
      expect(spectator.query('div .header .ssc')).toHaveText(`${item.ssc} - ${item.sscText}`);
    });

    it('should not render when editSsc is true', () => {
      spectator.setInput({ editSsc: true });
      expect(spectator.query('div .header .ssc')).not.toBeVisible();
    });
  });

  describe('div .ssc-wrapper .ssc-input', () => {
    it('should not render when editSsc is false', () => {
      spectator.setInput({ editSsc: false });
      expect(spectator.query('div .ssc-wrapper .ssc-input')).not.toBeVisible();
    });

    it('should render when editSsc is true', () => {
      spectator.setInput({ editSsc: true });
      expect(spectator.query('div .ssc-wrapper .ssc-input')).toBeVisible();
    });

    it('should render have ssc as input Text', async () => {
      const item = { ...orderItemMock, ssc: '000', sscText: 'SSC Text' };
      await spectator.setInput({ editSsc: true, item });
      expect(spectator.query('div .ssc-wrapper .ssc-input')).toHaveValue(item.ssc);
    });

    it('should call sscChange on value change', async () => {
      await spectator.setInput({ editSsc: true });
      spectator.component.sscChange = jasmine.createSpy();

      const testSsc = '000';
      await spectator.typeInElement(testSsc, 'div .ssc-wrapper .ssc-input');

      expect(spectator.component.sscChange).toHaveBeenCalledWith(testSsc);
    });
  });

  describe('div .ssc-wrapper .ssc-text', () => {
    it('should not render when editSsc is false', () => {
      spectator.setInput({ editSsc: false });
      expect(spectator.query('div .ssc-wrapper .ssc-text')).not.toBeVisible();
    });

    it('should render when editSsc is true', () => {
      spectator.setInput({ editSsc: true });
      expect(spectator.query('div .ssc-wrapper .ssc-text')).toBeVisible();
    });

    it('should render the sscText', async () => {
      const sscText = 'Test-sscText';
      await spectator.setInput({ editSsc: true, sscText });
      expect(spectator.query('div .ssc-wrapper .ssc-text')).toHaveText(sscText);
    });

    it('should add class .err when sscInvalid is true', () => {
      spectator.setInput({ editSsc: true, sscInvalid: true });
      expect(spectator.query('div .ssc-wrapper .ssc-text')).toHaveClass('err');
    });
  });

  describe('div .grid-container', () => {
    it('should render product-name', () => {
      const item = { ...orderItemMock, product: { contributors: 'Contributor1, Contributor2', name: 'ProductName' } };
      spectator.setInput({ item });

      const productName = `${item.product.contributors} - ${item.product.name}`;
      expect(spectator.query('div .grid-container .product-name')).toHaveText(productName);
    });

    it('should render specialComment', () => {
      const item = { ...orderItemMock, specialComment: 'TestComment' };
      spectator.setInput({ item });
      expect(spectator.query('div .grid-container .comment')).toHaveText(item.specialComment);
    });
  });

  describe('div .details', () => {
    it('should render supplier', () => {
      const item = { ...orderItemMock, supplier: 'TestSupplier' };
      spectator.setInput({ item });
      expect(spectator.query('div .details .supplier span')).toHaveText('Lieferant');
      expect(spectator.query('div .details .supplier strong')).toHaveText(item.supplier);
    });

    it('should render ean', () => {
      const item = { ...orderItemMock, product: { ean: '123456' } };
      spectator.setInput({ item });
      expect(spectator.query('div .details .ean')).toHaveText(item.product.ean);
    });

    it('should render ean', () => {
      const item = { ...orderItemMock, orderNumber: 'abc' };
      spectator.setInput({ item });
      expect(spectator.query('div .details .item-id span')).toHaveText('Vorgangs-ID');
      expect(spectator.query('div .details .item-id strong')).toHaveText(`${item.orderNumber}`);
    });

    it('should render price', () => {
      const item = { ...orderItemMock, price: 1.5 };
      spectator.setInput({ item });
      expect(spectator.query('div .details .price')).toHaveText(`${item.price}`);
    });

    it('should render quantity', () => {
      const item = { ...orderItemMock, quantity: 5 };
      spectator.setInput({ item });
      expect(spectator.query('div .details .quantity')).toHaveText(`${item.quantity}`);
    });

    it('should render estimatedShippingDate', () => {
      const item = { ...orderItemMock, estimatedShippingDate: new Date(2021, 7, 10).toISOString() };
      spectator.setInput({ item });
      expect(spectator.query('div .details .date span')).toHaveText('vsl. Lieferdatum');
      expect(spectator.query('div .details .date strong')).toHaveText('10.08.21');
    });

    describe('div .status', () => {
      it('should render processingStatus icon', () => {
        const item = { ...orderItemMock, processingStatus: 16 as OrderItemProcessingStatusValue };
        spectator.setInput({ item });
        expect(spectator.query('div .details .processing-status ui-icon')).toBeVisible();
      });

      it('should render processingStatus text', () => {
        const item = { ...orderItemMock, processingStatus: 16 as OrderItemProcessingStatusValue };
        spectator.setInput({ item });
        expect(spectator.query('div .details .status span')).toHaveText('Status');
        expect(spectator.query('div .details .processing-status')).toHaveText('bestellt');
      });
    });

    describe('div .format', () => {
      it('should not render when format is not set', () => {
        const item = { ...orderItemMock, product: { formatDetail: 'TestFormatDetail' } };
        spectator.setInput({ item });
        expect(spectator.query('div .details .format')).not.toBeVisible();
      });

      it('should render format-icon when format and formatDetail is set', () => {
        const item = { ...orderItemMock, product: { format: 'TestFormat', formatDetail: 'TestFormatDetail' } };
        spectator.setInput({ item });

        const src = '/assets/images/Icon_' + item.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 item = { ...orderItemMock, product: { format: 'TestFormat', formatDetail: 'TestFormatDetail' } };
        spectator.setInput({ item });
        expect(spectator.query('div .details .format-text')).toHaveText(item.product.formatDetail);
      });
    });
  });

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

    it('should be disabled when editSsc is true', () => {
      spectator.setInput({ editSsc: true });
      expect(spectator.query('button.cta-reorder')).toHaveAttribute('disabled');
    });

    it('should be enabled when editSsc is false', () => {
      spectator.setInput({ editSsc: false });
      expect(spectator.query('button.cta-reorder')).not.toHaveAttribute('disabled');
    });
  });

  describe('item input', () => {
    it('should set ssc to the orderItem ssc', () => {
      const item = { ssc: '1', product: {} };
      spectator.setInput({ item });
      expect(spectator.component.ssc).toBe(item.ssc);
    });

    it('should set sscText to the orderItem sscText', () => {
      const item = { sscText: 'Test', product: {} };
      spectator.setInput({ item });
      expect(spectator.component.sscText).toBe(item.sscText);
    });
  });

  describe('sscChange()', () => {
    it('should set ssc to the passed value', () => {
      const value = '999';
      spectator.component.sscChange(value);
      expect(spectator.component.ssc).toBe(value);
    });

    it('should set sscChanged to true', () => {
      spectator.component.sscChange('1');
      expect(spectator.component.sscChanged).toBe(true);
    });

    it('should call omsService.getStockStatusCodes with orderItem supplierId', () => {
      const item = { ...orderItemMock, supplierId: 1 };
      spectator.setInput({ item });
      spectator.component.sscChange('1');
      expect(domainOmsServiceMock.getStockStatusCodes).toHaveBeenCalledWith(item.supplierId);
    });

    it('should set sscText to "Meldenummer ist ungültig" when no code was found', async () => {
      domainOmsServiceMock.getStockStatusCodes = jasmine.createSpy().and.returnValue(Promise.resolve([]));
      await spectator.component.sscChange('1');
      expect(spectator.component.sscText).toBe('Meldenummer ist ungültig');
    });

    it('should set sscText to supplierDescription of the found statusCode and sscInvalid to false', async () => {
      const testCode = { code: '1', supplierDescription: 'Test supplier description' };
      domainOmsServiceMock.getStockStatusCodes = jasmine.createSpy().and.returnValue(Promise.resolve([testCode]));
      await spectator.component.sscChange(testCode.code);
      expect(spectator.component.sscText).toBe(testCode.supplierDescription);
      expect(spectator.component.sscInvalid).toBe(false);
    });

    it('should set sscInvalid to true when no code was found', async () => {
      domainOmsServiceMock.getStockStatusCodes = jasmine.createSpy().and.returnValue(Promise.resolve([]));
      await spectator.component.sscChange('1');
      expect(spectator.component.sscInvalid).toBe(true);
    });
  });

  describe('sscUpdated()', () => {
    it('should reset sscText to the orderItem sscText', () => {
      const item = { ...orderItemMock, ssc: '000', sscText: 'SSC Text' };
      spectator.setInput({ item });
      spectator.component.sscText = '0';
      spectator.component.sscUpdated();
      expect(spectator.component.sscText).toBe(item.sscText);
    });

    it('should reset ssc to the orderItem ssc', () => {
      const item = { ...orderItemMock, ssc: '000', sscText: 'SSC Text' };
      spectator.setInput({ item });
      spectator.component.ssc = '0';
      spectator.component.sscUpdated();
      expect(spectator.component.ssc).toBe(item.ssc);
    });

    it('should reset sscInvalid to false', () => {
      spectator.component.sscInvalid = true;
      spectator.component.sscUpdated();
      expect(spectator.component.sscInvalid).toBe(false);
    });

    it('should reset sscChanged to false', () => {
      spectator.component.sscChanged = true;
      spectator.component.sscUpdated();
      expect(spectator.component.sscChanged).toBe(false);
    });
  });

  describe('showReorderModal()', () => {
    it('should call uiModal open and pass the orderItem', () => {
      uiModalServiceMock.open = jasmine.createSpy().and.returnValue({ afterClosed$: new Subject() });
      spectator.component.showReorderModal();
      expect(uiModalServiceMock.open).toHaveBeenCalledWith({ content: GoodsInListReorderModalComponent, data: orderItemMock });
    });
  });
});
