import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { SearchStateFacade } from '@shelf-store';
import { of, Subject } from 'rxjs';
import { ShelfNavigationService, ShelfSearchFacadeService } from '../../../shared/services';
import { ShelfSearchInputComponent } from './search-input.component';
import { ShelfFilterService } from '../../../services';
import { ShelfSearchbarComponent } from '../../../components';
import { By } from '@angular/platform-browser';

class MockSearchStateFacade {
  constructor() {}

  get hasResults$() {
    return of(true);
  }

  get hits$() {
    return of(3);
  }

  get input$() {
    return of('Testsuche');
  }

  get result$() {
    return of([]);
  }

  get fetching$() {
    return of(false);
  }

  get showNoResultError$() {
    return of(true);
  }

  get selectedFilters$() {
    return of([]);
  }

  get pendingInput$() {
    return of('Pending Input');
  }

  setPendingInput() {}

  clearResult() {}

  clearError() {}
}

fdescribe('#SearchInputComponent', () => {
  let fixture: ComponentFixture<ShelfSearchInputComponent>;
  let component: ShelfSearchInputComponent;
  let facade: SearchStateFacade;
  let navigationService: jasmine.SpyObj<ShelfNavigationService>;
  let shelfSearchService: jasmine.SpyObj<ShelfSearchFacadeService>;
  let filterService: jasmine.SpyObj<ShelfFilterService>;

  beforeEach(async () => {
    TestBed.configureTestingModule({
      declarations: [ShelfSearchInputComponent],
      providers: [
        {
          provide: SearchStateFacade,
          useClass: MockSearchStateFacade,
        },
        {
          provide: ShelfNavigationService,
          useValue: jasmine.createSpyObj('shelfNavigationService', ['navigateToResultList', 'updateResultPageBreadcrumb']),
        },
        {
          provide: ShelfFilterService,
          useValue: jasmine.createSpy('shelfFilterService'),
        },
        {
          provide: ShelfSearchFacadeService,
          useValue: jasmine.createSpyObj('shelfSearchService', ['search']),
        },
      ],
      schemas: [NO_ERRORS_SCHEMA],
    });
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(ShelfSearchInputComponent);
    component = fixture.componentInstance;
    facade = TestBed.inject(SearchStateFacade);
    navigationService = TestBed.inject(ShelfNavigationService);
    shelfSearchService = TestBed.inject(ShelfSearchFacadeService);
    filterService = TestBed.inject(ShelfFilterService);

    component.hasAutocomplete = false;
    component.autocompleteResult$ = of([]);
    component.searchbar = new ShelfSearchbarComponent();
    spyOn(component.searchbar, 'setInputValue').and.callFake(() => {});
    spyOn(component.searchbar, 'setFocus').and.callFake(() => {});

    filterService.overlayClosed$ = new Subject();

    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });

  it('should not clear the previous results onInit', () => {
    spyOn(facade, 'clearResult').and.callThrough();
    component.ngOnInit();

    expect(facade.clearResult).not.toHaveBeenCalled();
  });

  it('should not setup navigation onInit', () => {
    component.ngOnInit();

    fixture.detectChanges();

    expect(navigationService.navigateToResultList).not.toHaveBeenCalled();
  });

  it('should set up navigation after triggering a search', async () => {
    shelfSearchService.search.and.returnValue(Promise.resolve());
    await component.triggerSearch({ type: 'search', value: 'Testsuche' });

    fixture.detectChanges();

    expect(navigationService.navigateToResultList).toHaveBeenCalled();
  });

  it('should update the searchbar when the pending input changes', fakeAsync(() => {
    spyOnProperty(facade, 'pendingInput$').and.returnValue(of('Testsuche'));

    component.ngAfterViewInit();
    component.ngOnInit();
    tick();
    fixture.detectChanges();

    expect(component.searchbar.setInputValue).toHaveBeenCalledWith('Testsuche');
  }));

  describe('onClickOutside', () => {
    let searchbar: HTMLElement;

    beforeEach(() => {
      searchbar = fixture.debugElement.query(By.css('app-shelf-searchbar')).nativeElement;
      spyOn(component, 'onClickOutside').and.callThrough();
      spyOn(component.showAutocompleteResults$, 'next').and.callThrough();
    });

    it('should set showAutocompleteResult to false', () => {
      component.onClickOutside();
      fixture.detectChanges();

      expect(component.showAutocompleteResults$.next).toHaveBeenCalledWith(false);
    });

    it('should be called when clicking outside', () => {
      searchbar.dispatchEvent(new Event('clickOutside'));
      fixture.detectChanges();

      expect(component.showAutocompleteResults$.next).toHaveBeenCalledWith(false);
    });
  });

  describe('searchbar', () => {
    let searchbar: HTMLElement;

    beforeEach(() => {
      searchbar = fixture.debugElement.query(By.css('app-shelf-searchbar')).nativeElement;
    });

    it('should be in the component', () => {
      expect(searchbar).toBeTruthy();
    });

    it('should have the ClickOutsideDirective', () => {
      expect(searchbar.hasAttribute('clickOutside')).toBeTruthy();
    });
  });
});
