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

import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { BciSharedModule, MasterDetailViewComponent, ModalWindowService } from '@bci-web-core/core';
import {
  GenericMapsModule,
  LocationMapModule,
  LocationMapService,
  LocationService,
  LocationStyleService,
  LsLocation,
  OpenLayersMapComponent,
  OpenLayersStyleFactory,
} from '@bci-web-core/maps';
import { LocationServiceMock } from '../location-service-mock.service';
import { DemoLocationListComponent } from './demo-location-list.component';

describe('DemoLocationListComponent', () => {
  let component: DemoLocationListComponent;
  let fixture: ComponentFixture<DemoLocationListComponent>;

  beforeEach(
    waitForAsync(() => {
      return TestBed.configureTestingModule({
        imports: [
          BciSharedModule,
          GenericMapsModule,
          LocationMapModule,
          RouterTestingModule,
          NoopAnimationsModule,
          MatTabsModule,
          MatTableModule,
          MatCheckboxModule,
          MatIconModule,
          MatButtonToggleModule,
        ],
        providers: [
          { provide: LocationService, useClass: LocationServiceMock },
          LocationStyleService,
          OpenLayersMapComponent,
          OpenLayersStyleFactory,
          { provide: ModalWindowService, useValue: {} },
        ],
        declarations: [DemoLocationListComponent],
      });
      TestBed.overrideProvider(LocationMapService, {
        useValue: {
          selectedLocations: [],
        },
      });
      TestBed.compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(DemoLocationListComponent);
    component = fixture.componentInstance;
    fixture.autoDetectChanges();
  });

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

  describe('Navigation', () => {
    let activatedRoute: ActivatedRoute;
    let router: Router;
    let routerNavigateSpy: jest.SpyInstance;

    beforeEach(() => {
      activatedRoute = TestBed.inject(ActivatedRoute);
      router = TestBed.inject(Router);
      routerNavigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true));
      jest.mock('rxjs/operators', () => {
        const operators = jest.requireActual('rxjs/operators');
        operators.delay = jest.fn(() => (s) => s); // <= mock delay
        return operators;
      });
    });

    it('should navigate to location details, when one location is selected', () => {
      component.selected([{ id: 'a', name: 'A', type: 'a' }]);
      expect(routerNavigateSpy).toHaveBeenCalledWith(['a'], { relativeTo: activatedRoute });
    });

    it('should leave location details, when none is selected', () => {
      component.masterDetailView = <MasterDetailViewComponent>{
        isDetailsShown: true,
      };
      component.selected([]);
      expect(routerNavigateSpy).toHaveBeenCalledWith(['.'], { relativeTo: activatedRoute });
    });

    it(
      'should not navigate if no location details shown and when no location is selected',
      fakeAsync(() => {
        component.masterDetailView = <MasterDetailViewComponent>{
          isDetailsShown: false,
        };
        component.selected([]);
        expect(routerNavigateSpy).not.toHaveBeenCalledWith();
      })
    );

    it('should leave location details, when more than one is selected', () => {
      component.masterDetailView = <MasterDetailViewComponent>{
        isDetailsShown: true,
      };
      component.selected([
        { id: 'a', name: 'A', type: 'a' },
        { id: 'b', name: 'B', type: 'b' },
      ]);
      expect(routerNavigateSpy).toHaveBeenCalledWith(['.'], { relativeTo: activatedRoute });
    });

    it('should not navigate if no location details are open and when more than one location is selected', () => {
      component.masterDetailView = <MasterDetailViewComponent>{
        isDetailsShown: false,
      };
      component.selected([
        { id: 'a', name: 'A', type: 'a' },
        { id: 'b', name: 'B', type: 'b' },
      ]);
      expect(routerNavigateSpy).not.toHaveBeenCalled();
    });
  });

  describe('Toggle in single selection mode', () => {
    const locationA: LsLocation = { id: 'a', name: 'A', type: 'a' };
    const locationB: LsLocation = { id: 'b', name: 'B', type: 'b' };
    let locationMapService: LocationMapService;

    beforeEach(() => {
      locationMapService = fixture.debugElement.injector.get(LocationMapService);
      // eslint-disable-next-line no-empty-function
      jest.spyOn((<any>component).router, 'navigate').mockImplementation(() => {});
    });

    it(
      'should toggle on when nothing select before',
      fakeAsync(() => {
        component.toggleSelectionSingle(locationA);

        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationA]);
      })
    );

    it(
      'should toggle off when location already selected',
      fakeAsync(() => {
        locationMapService.selectedLocations = [locationA];
        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationA]);

        component.toggleSelectionSingle(locationA);

        tick(500);
        expect(locationMapService.selectedLocations).toEqual([]);
      })
    );

    it(
      'should switch selected location when another location is already selected',
      fakeAsync(() => {
        locationMapService.selectedLocations = [locationB];
        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationB]);

        component.toggleSelectionSingle(locationA);

        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationA]);
      })
    );
  });

  describe('Toggle in multi selection mode', () => {
    const locationA: LsLocation = { id: 'a', name: 'A', type: 'a' };
    const locationB: LsLocation = { id: 'b', name: 'B', type: 'b' };
    let locationMapService: LocationMapService;

    beforeEach(() => {
      locationMapService = fixture.debugElement.injector.get(LocationMapService);
      // eslint-disable-next-line no-empty-function
      jest.spyOn((<any>component).router, 'navigate').mockImplementation(() => {});
    });

    it(
      'should toggle on when nothing select before',
      fakeAsync(() => {
        component.toggleSelectionMulti(locationA);

        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationA]);
      })
    );

    it(
      'should toggle off when location already selected',
      fakeAsync(() => {
        locationMapService.selectedLocations = [locationA, locationB];
        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationA, locationB]);

        component.toggleSelectionMulti(locationA);

        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationB]);
      })
    );

    it(
      'should switch selected location when another location is already selected',
      fakeAsync(() => {
        locationMapService.selectedLocations = [locationB];
        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationB]);

        component.toggleSelectionMulti(locationA);

        tick(500);
        expect(locationMapService.selectedLocations).toEqual([locationB, locationA]);
      })
    );
  });
});
