/*
 * Copyright (C) 2018 Robert Bosch GmbH Copyright (C) 2018 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

import { CommonModule } from '@angular/common';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatBadgeModule } from '@angular/material/badge';
import { MatInputModule } from '@angular/material/input';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { BCI_SHARED_INTL_PROVIDER } from '../shared-intl';
import { CommandbarButtonComponent } from './button/commandbar-button.component';

import { CommandbarComponent } from './commandbar.component';

class CommandbarFixtureComponentObject {
  private fixture: ComponentFixture<CommandbarComponent>;

  constructor(fixture: ComponentFixture<CommandbarComponent>) {
    this.fixture = fixture;
  }

  getEntryCount() {
    return this.fixture.nativeElement.querySelector('.entry-count');
  }
}

describe('CommandbarComponent', () => {
  let component: CommandbarComponent;
  let fixture: ComponentFixture<CommandbarComponent>;
  let co: CommandbarFixtureComponentObject;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        imports: [
          MatIconModule,
          ReactiveFormsModule,
          MatToolbarModule,
          CommonModule,
          MatFormFieldModule,
          MatInputModule,
          MatTooltipModule,
          MatBadgeModule,
          NoopAnimationsModule,
        ],
        declarations: [CommandbarComponent, CommandbarButtonComponent],
        providers: [BCI_SHARED_INTL_PROVIDER],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(CommandbarComponent);
    co = new CommandbarFixtureComponentObject(fixture);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

  describe('search', () => {
    it('should debounce typed search', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));

      component.searchForm.controls.search.setValue('a');
      tick(50);
      component.searchForm.controls.search.setValue('b');

      expect(searches).toEqual([]);
      tick(300);
      expect(searches).toEqual(['b']);
    }));

    it('should immediately search when enter is pressed', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));
      const searchFieldDebugElement = fixture.debugElement.query(By.css('[formControlName="search"]'));

      component.searchForm.controls.search.setValue('a');
      searchFieldDebugElement.triggerEventHandler('keyup.enter', null);

      expect(searches).toEqual(['a']);

      component.searchForm.controls.search.setValue('b');
      searchFieldDebugElement.triggerEventHandler('keyup.enter', null);

      expect(searches).toEqual(['a', 'b']);

      tick(1000);
    }));

    it('should only emit distinct typed searches', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));

      component.searchForm.controls.search.setValue('a');

      expect(searches).toEqual([]);
      tick(300);
      expect(searches).toEqual(['a']);

      component.searchForm.controls.search.setValue('a');

      expect(searches).toEqual(['a']);
      tick(300);
      expect(searches).toEqual(['a']);

      component.searchForm.controls.search.setValue('b');

      expect(searches).toEqual(['a']);
      tick(300);
      expect(searches).toEqual(['a', 'b']);
    }));

    it('should immediately emit explicit search start', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));

      component.searchForm.controls.search.setValue('a');
      component.searchSubmit();

      expect(searches).toEqual(['a']);
      tick();
      expect(searches).toEqual(['a']);
      tick(1000);
    }));

    it('should only emit distinct explicit searches', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));

      component.searchForm.controls.search.setValue('a');
      component.searchSubmit();
      tick(10);
      component.searchSubmit();
      tick(10);
      component.searchForm.controls.search.setValue('b');
      component.searchSubmit();

      expect(searches).toEqual(['a', 'b']);
      tick();
      expect(searches).toEqual(['a', 'b']);
      tick(1000);
    }));

    it('should emit search when input is set before onInit', fakeAsync(() => {
      const searches = [];

      component.searchTerm = 'test';
      fixture.detectChanges();
      tick(10);

      component.search.subscribe((search) => searches.push(search));
      expect(searches).toEqual([]);

      component.ngOnInit();

      expect(searches).toEqual(['test']);
      tick(1000);
    }));

    it('should emit search when input is set', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));

      component.searchTerm = 'test';
      fixture.detectChanges();
      tick(10);
      expect(searches).toEqual(['test']);
      tick(1000);
    }));

    it('should clear search when input is cleared', fakeAsync(() => {
      const searches = [];
      component.search.subscribe((search) => searches.push(search));

      component.searchTerm = 'test';
      fixture.detectChanges();
      tick(10);
      expect(searches).toEqual(['test']);

      component.searchTerm = null;
      fixture.detectChanges();
      tick(200);
      expect(searches).toEqual(['test', null]);

      component.searchTerm = 'test2';
      fixture.detectChanges();
      tick(200);
      expect(searches).toEqual(['test', null, 'test2']);

      component.searchTerm = '';
      fixture.detectChanges();
      tick(200);
      expect(searches).toEqual(['test', null, 'test2', '']);
      tick(1000);
    }));
  });

  describe('item count', () => {
    it('should not show item count if not set', () => {
      expect(co.getEntryCount()).toBeNull();
    });

    it('should show "0" for 0 items', () => {
      component.itemCount = 0;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('0');
    });

    it('should show "1" for 1 item', () => {
      component.itemCount = 1;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('1');
    });

    it('should show "2" for 2 items', () => {
      component.itemCount = 2;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('2');
    });

    it('should not show item count for 0 selected items (item count undefined)', () => {
      component.selectedItemCount = 0;
      fixture.detectChanges();
      expect(co.getEntryCount()).toBeNull();
    });

    it('should show "1" for 1 selected item (item count undefined)', () => {
      component.selectedItemCount = 1;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('1');
    });

    it('should show "2" for 2 selected items (item count undefined)', () => {
      component.selectedItemCount = 2;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('2');
    });

    it('should show "0" for 0 items and 0 selected items', () => {
      component.itemCount = 0;
      component.selectedItemCount = 0;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('0');
    });

    it('should show "1" for 1 item and 0 selected item', () => {
      component.itemCount = 1;
      component.selectedItemCount = 0;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('1');
    });

    it('should show "1/2" for 1 item and 1 selected item', () => {
      component.itemCount = 1;
      component.selectedItemCount = 1;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('1/1');
    });

    it('should show "1/2" for 2 items and 1 selected item', () => {
      component.itemCount = 2;
      component.selectedItemCount = 1;
      fixture.detectChanges();
      expect(co.getEntryCount().textContent).toMatch('1/2');
    });
  });
});
