import { createComponentFactory, Spectator } from '@ngneat/spectator';
import { UiAutocompleteModule } from '../../../autocomplete';
import { UiSearchboxModule } from '../../../searchbox';
import { IUiInput, IUiInputGroup, UiInput, UiInputGroup, UiInputType } from '../../tree';

import { isObservable, NEVER, of } from 'rxjs';
import { UiFilterInputGroupMainComponent } from './filter-input-group-main.component';

describe('UiFilterInputGroupMainComponent', () => {
  const inputGroupData: IUiInputGroup = {
    input: [{ key: 'qs', placeholder: 'ISBN/EAN, Titel, Kundenname', type: 1, constraint: '^.{2,100}$' }],
  };

  let spectator: Spectator<UiFilterInputGroupMainComponent>;

  const createSpectator = createComponentFactory({
    component: UiFilterInputGroupMainComponent,
    imports: [UiSearchboxModule, UiAutocompleteModule],
  });

  beforeEach(() => {
    spectator = createSpectator();
  });

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

  describe('uiInput', () => {
    it('should return undefined when inputGroup is not set', () => {
      expect(spectator.component.uiInput).toBeUndefined();
    });

    it('should return an instance of UiInput', () => {
      spectator.setInput({
        inputGroup: inputGroupData,
      });
      expect(spectator.component.uiInput instanceof UiInput).toBeTruthy();
    });

    it('should return the same instance of the input', () => {
      const instance = UiInputGroup.create(inputGroupData);
      spectator.setInput({
        inputGroup: instance,
      });

      expect(spectator.component.uiInput).toBe(instance.input[0]);
    });
  });

  describe('ngOnInit()', () => {
    it('should set the autocompleteProvider with the property for to be main', () => {
      const provider = { for: 'main', complete: () => NEVER };
      spectator.component['autocompleteProviders'] = [provider, { for: 'not main', complete: () => NEVER }];
      spectator.component.ngOnInit();
      expect(spectator.component.autocompleteProvider).toBe(provider);
    });

    it('should set the scanProvider with the property for to be main', () => {
      const provider = { for: 'main', scan: () => NEVER };
      spectator.component['scanProviders'] = [provider, { for: 'not main', scan: () => NEVER }];
      spectator.component.ngOnInit();
      expect(spectator.component.scanProvider).toBe(provider);
    });
  });

  describe('initAutocomplete()', () => {
    it('should create an Observable for autocompleteResult$', () => {
      spectator.setInput({
        inputGroup: inputGroupData,
      });
      spectator.component.setAutocompleteProvider({ for: 'main', complete: () => NEVER });
      spectator.component.initAutocomplete();
      expect(isObservable(spectator.component.autocompleteResults$)).toBe(true);
    });

    it('should call autocompleteComponent.open() if the prvider returns at least one result', () => {
      spectator.setInput({
        inputGroup: inputGroupData,
      });
      spectator.component.setAutocompleteProvider({
        for: 'main',
        complete: (query) => of([{ display: `${query} first` }, { display: `${query} second` }]),
      });
      spectator.detectComponentChanges();
      spyOn(spectator.component.autocompleteComponent, 'open');
      spectator.component.complete.next('test');
      expect(spectator.component.autocompleteComponent.open).toHaveBeenCalled();
    });

    it('should call autocompleteComponent.close() if the prvider returns at no results', () => {
      spectator.setInput({
        inputGroup: inputGroupData,
      });
      spectator.component.setAutocompleteProvider({
        for: 'main',
        complete: (query) => of([]),
      });
      spectator.detectComponentChanges();
      spyOn(spectator.component.autocompleteComponent, 'close');
      spectator.component.complete.next('test');
      expect(spectator.component.autocompleteComponent.close).toHaveBeenCalled();
    });
  });

  describe('ui-searchbox', () => {
    describe('input.ui-searchbox-input', () => {
      it('should have a placeholder', () => {
        spectator.setInput({
          inputGroup: { input: [{ type: UiInputType.Text, placeholder: 'Unit Test Placeholder' }] },
        });
        spectator.detectComponentChanges();

        expect(spectator.query('input.ui-searchbox-input')).toContainProperty('placeholder', 'Unit Test Placeholder');
      });

      it('should have a value', async () => {
        spectator.setInput({
          inputGroup: { input: [{ type: UiInputType.Text, value: 'Unit Test Value' }] },
        });

        spectator.detectComponentChanges();
        await spectator.fixture.whenStable();
        expect(spectator.query('input.ui-searchbox-input')).toHaveValue('Unit Test Value');
      });

      it('should call input.setValue when value changes', async () => {
        spectator.setInput({
          inputGroup: { input: [{ type: UiInputType.Text, value: 'Unit Test Value' }] },
        });
        spyOn(spectator.component.uiInput, 'setValue');
        spectator.detectComponentChanges();
        await spectator.fixture.whenStable();
        spectator.typeInElement('Unit Test', spectator.query('input.ui-searchbox-input'));
        expect(spectator.component.uiInput.setValue).toHaveBeenCalledWith('Unit Test');
      });
    });
  });

  describe('ui-autocomplete', () => {
    it('should not render the element if no autocompleteProvider is set', () => {
      spyOnProperty(spectator.component, 'autocompleteProvider', 'get').and.returnValue(undefined);
      spectator.detectComponentChanges();
      expect(spectator.query('ui-autocomplete')).toBeNull();
    });

    it('should render the element if autocompleteProvider is set', () => {
      spectator.setInput({ inputGroup: inputGroupData });
      spyOnProperty(spectator.component, 'autocompleteProvider', 'get').and.returnValue({ for: 'main', complete: () => NEVER });
      spectator.detectComponentChanges();
      expect(spectator.query('ui-autocomplete')).not.toBeNull();
    });
  });
});
