import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
import { fakeAsync } from '@angular/core/testing';
import { createHostFactory, Spectator } from '@ngneat/spectator';
import { UiAutocompleteItemComponent } from './autocomplete-item.component';
import { UiAutocompleteComponent } from './autocomplete.component';

describe('UiSearchboxAutocomplete', () => {
  let spectator: Spectator<UiAutocompleteComponent>;
  const createComponent = createHostFactory({
    component: UiAutocompleteComponent,
    declarations: [UiAutocompleteItemComponent],
  });

  beforeEach(
    () =>
      (spectator = createComponent(`
  <ui-autocomplete>
    <button [uiAutocompleteItem]>My Autocomplete Item</button>
  </ui-autocomplete>`))
  );

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

  describe('ngAfterContentInit', () => {
    it('should create an instance of ActiveDescendantKeyManager and set it to listKeyManager', () => {
      spyOn(spectator.component, 'registerItemOnClick');
      spyOn(spectator.component.subscriptions, 'add');
      spectator.component.ngAfterContentInit();
      expect(spectator.component.listKeyManager instanceof ActiveDescendantKeyManager).toBe(true);
      expect(spectator.component.registerItemOnClick).toHaveBeenCalledTimes(1);
      expect(spectator.component.subscriptions.add).toHaveBeenCalledTimes(1);
    });
  });

  describe('ngOnDestroy', () => {
    it('should call unsubscribe', () => {
      spyOn(spectator.component.subscriptions, 'unsubscribe');
      spectator.component.ngOnDestroy();
      expect(spectator.component.subscriptions.unsubscribe).toHaveBeenCalled();
    });
  });

  describe('handleKeyboardEvent', () => {
    it('should emit selectItem and call close and not call listKeyManager.onKeyDown on Enter key', () => {
      spyOn(spectator.component, 'close');
      spyOn(spectator.component.listKeyManager, 'onKeydown');
      spyOnProperty(spectator.component, 'activeItem', 'get').and.returnValue({ item: 'Test' });

      let selectItem;
      spectator.output('selectItem').subscribe((item) => (selectItem = item));

      spectator.component.opend = true;
      const event = new KeyboardEvent('keyup', { key: 'Enter' });
      spectator.component.handleKeyboardEvent(event);

      expect(spectator.component.close).toHaveBeenCalled();
      expect(selectItem).toEqual({ item: 'Test' });
      expect(spectator.component.listKeyManager.onKeydown).not.toHaveBeenCalledWith(event);
    });

    it('should call close and not emit selectItem and not call listKeyManager.onKeydown on Escape key', () => {
      spyOn(spectator.component, 'close');
      spyOn(spectator.component.listKeyManager, 'onKeydown');
      spyOnProperty(spectator.component, 'activeItem', 'get').and.returnValue({ item: 'Test' });

      let selectItem;
      spectator.output('selectItem').subscribe((item) => (selectItem = item));

      spectator.component.opend = true;
      const event = new KeyboardEvent('keyup', { key: 'Escape' });
      spectator.component.handleKeyboardEvent(event);

      expect(spectator.component.close).toHaveBeenCalled();
      expect(selectItem).toBeUndefined();
      expect(spectator.component.listKeyManager.onKeydown).not.toHaveBeenCalledWith(event);
    });

    it('should call listKeyManager.onKeyDown and not emit selectItem and not call close on ArrowDown key', () => {
      spyOn(spectator.component, 'close');
      spyOn(spectator.component.listKeyManager, 'onKeydown');
      spyOnProperty(spectator.component, 'activeItem', 'get').and.returnValue({ item: 'Test' });

      let selectItem;
      spectator.output('selectItem').subscribe((item) => (selectItem = item));

      spectator.component.opend = true;
      const event = new KeyboardEvent('keyup', { key: 'ArrowDown' });
      spectator.component.handleKeyboardEvent(event);

      expect(spectator.component.close).not.toHaveBeenCalled();
      expect(selectItem).toBeUndefined();
      expect(spectator.component.listKeyManager.onKeydown).toHaveBeenCalledWith(event);
    });

    it('should not call close, listKeyManager.onKeyDown and not emit selectItem if opened is false', () => {
      spyOn(spectator.component, 'close');
      spyOn(spectator.component.listKeyManager, 'onKeydown');
      spyOnProperty(spectator.component, 'activeItem', 'get').and.returnValue({ item: 'Test' });

      let selectItem;
      spectator.output('selectItem').subscribe((item) => (selectItem = item));

      spectator.component.opend = false;
      const event = new KeyboardEvent('keyup', { key: 'ArrowDown' });
      spectator.component.handleKeyboardEvent(event);

      expect(spectator.component.close).not.toHaveBeenCalled();
      expect(selectItem).toBeUndefined();
      expect(spectator.component.listKeyManager.onKeydown).not.toHaveBeenCalledWith(event);
    });
  });

  describe('open', () => {
    it('should set opened to true', () => {
      spectator.component.open();
      expect(spectator.component.opend).toBe(true);
    });
  });

  describe('close', () => {
    it('should set opened to false and call listKeyManager.setActiveItem(undefined)', () => {
      spyOn(spectator.component.listKeyManager, 'setActiveItem');
      spectator.component.close();
      expect(spectator.component.opend).toBe(false);
      expect(spectator.component.listKeyManager.setActiveItem).toHaveBeenCalledWith(undefined);
    });
  });

  describe('registerItemOnClick()', () => {
    it('should call registerOnClick for each item', () => {
      const items = spectator.component.items;
      items.forEach((item) => spyOn(item, 'registerOnClick'));
      spectator.component.registerItemOnClick();
      items.forEach((item) => expect(item.registerOnClick).toHaveBeenCalled());
    });
  });
});
