import { Spectator, createComponentFactory, SpyObject, createSpyObject } from '@ngneat/spectator';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { Config } from '@core/config';
import { ApplicationService } from '@core/application';
import { of } from 'rxjs';
import { Renderer2 } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ServiceWorkerModule, SwUpdate } from '@angular/service-worker';
import { NotificationsHub } from '@hub/notifications';
import { discardPeriodicTasks, fakeAsync, flush, tick } from '@angular/core/testing';

describe('AppComponent', () => {
  let spectator: Spectator<AppComponent>;
  let config: SpyObject<Config>;
  let renderer: SpyObject<Renderer2>;
  let applicationServiceMock: SpyObject<ApplicationService>;
  let notificationsHubMock: SpyObject<NotificationsHub>;
  let swUpdateMock: SpyObject<SwUpdate>;
  const createComponent = createComponentFactory({
    component: AppComponent,
    imports: [CommonModule, RouterTestingModule],
    providers: [],
    mocks: [Config, SwUpdate],
  });

  beforeEach(() => {
    applicationServiceMock = createSpyObject(ApplicationService);
    applicationServiceMock.getSection$.and.returnValue(of('customer'));
    applicationServiceMock.getActivatedProcessId$.and.returnValue(of(undefined));
    renderer = jasmine.createSpyObj('Renderer2', ['addClass', 'removeClass']);

    notificationsHubMock = createSpyObject(NotificationsHub);
    notificationsHubMock.notifications$ = of({});
    swUpdateMock = createSpyObject(SwUpdate);

    spectator = createComponent({
      providers: [
        { provide: ApplicationService, useValue: applicationServiceMock },
        {
          provide: Renderer2,
          useValue: renderer,
        },
        { provide: NotificationsHub, useValue: notificationsHubMock },
        { provide: SwUpdate, useValue: swUpdateMock },
      ],
    });
    config = spectator.inject(Config);
  });

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

  it('should have a router outlet', () => {
    expect(spectator.query('router-outlet')).toExist();
  });

  describe('ngOnInit', () => {
    it('should call setTitle', () => {
      const spy = spyOn(spectator.component, 'setTitle');
      spectator.component.ngOnInit();
      expect(spy).toHaveBeenCalled();
    });

    it('should call logVersion', () => {
      const spy = spyOn(spectator.component, 'logVersion');
      spectator.component.ngOnInit();
      expect(spy).toHaveBeenCalled();
    });
  });

  describe('setTitle', () => {
    it('should call Title.setTitle()', () => {
      const spyTitleSetTitle = spyOn(spectator.component['_title'], 'setTitle');
      config.get.and.returnValue('test');
      spectator.component.setTitle();
      expect(spyTitleSetTitle).toHaveBeenCalledWith('test');
    });
  });

  describe('logVersion', () => {
    it('should call console.log()', () => {
      const spyConsoleLog = spyOn(console, 'log');
      config.get.and.returnValue('test');
      spectator.component.logVersion();
      expect(spyConsoleLog).toHaveBeenCalled();
    });
  });

  // --------------------------------------------------------
  // Unit Tests Implementation for Angular Version 13.x.x

  // describe('updateClient()', () => {
  //   it('should call checkForUpdate() if SwUpdate.isEnabled is True', () => {
  //     spyOn(spectator.component, 'checkForUpdate');
  //     spyOn(spectator.component, 'initialCheckForUpdate');
  //     (swUpdateMock as any).isEnabled = true;
  //     spectator.component.updateClient();
  //     expect(spectator.component.initialCheckForUpdate).toHaveBeenCalled();
  //     expect(spectator.component.checkForUpdate).toHaveBeenCalled();
  //   });

  //   it('should not call checkForUpdate() if SwUpdate.isEnabled is False', () => {
  //     spyOn(spectator.component, 'checkForUpdate');
  //     spyOn(spectator.component, 'initialCheckForUpdate');
  //     (swUpdateMock as any).isEnabled = false;
  //     spectator.component.updateClient();
  //     expect(spectator.component.initialCheckForUpdate).not.toHaveBeenCalled();
  //     expect(spectator.component.checkForUpdate).not.toHaveBeenCalled();
  //   });
  // });

  // describe('checkForUpdate', () => {
  //   it('should call swUpdate.checkForUpdate() and notifications.updateNotification() every second', fakeAsync(() => {
  //     swUpdateMock.checkForUpdate.and.returnValue(Promise.resolve());
  //     spectator.component.checkForUpdates = 1000;
  //     spectator.component.checkForUpdate();

  //     spectator.detectChanges();
  //     tick(1100);

  //     expect(notificationsHubMock.updateNotification).toHaveBeenCalled();
  //     discardPeriodicTasks();
  //   }));
  // });

  // describe('initialCheckForUpdate', () => {
  //   it('should call swUpdate.checkForUpdate()', () => {
  //     swUpdateMock.checkForUpdate.and.returnValue(new Promise(undefined));
  //     spectator.component.initialCheckForUpdate();
  //     expect(swUpdateMock.checkForUpdate).toHaveBeenCalled();
  //   });
  // });
});
