import { Injectable, OnDestroy } from '@angular/core';
import { SsoService } from 'sso';
import { Store } from '@ngxs/store';
import { CountrySelector } from '../store/selectors/countries.selector';
import { LoadCountries } from '../store/actions/countries.actions';
import { AppState } from '../store/state/app.state';
import { LoadBranches, LoadUserBranch } from '../store/actions/branch.actions';
import { AppUserDataSync, ReloadSavedState } from '../store/actions/app.actions';
import { Subject, of, Observable, timer } from 'rxjs';
import { takeUntil, catchError, take, filter, pairwise } from 'rxjs/operators';
import { UserStateService } from './user-state.service';
import { Meta, MetaDefinition } from '@angular/platform-browser';
import { ActivatedRoute, Router, RoutesRecognized } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { isNullOrUndefined } from 'util';
import smoothscroll from 'smoothscroll-polyfill';
import { WindowRef } from './window-ref.service';
import { LoadVats } from '../store/actions/vat.actions';
import { LoadSuppliers } from '../store/actions/suppliers.actions';
import { RemissionSelectors } from '../store/selectors/remission.selectors';
import { Idle, DEFAULT_INTERRUPTSOURCES } from '@ng-idle/core';
import { AppConfiguration } from '../../app-configuration';

declare let ga: Function;
export const NOT_IDLE_REMISSION_REMINDER_MINUTES = 20;
export const IDLE_REMISSION_REMINDER_MINUTES = 40;
@Injectable({
  providedIn: 'root',
})
export class AppService implements OnDestroy {
  destroy$ = new Subject();
  loader$ = new Subject();
  leavingRemission$ = new Subject();
  remissionReminder$ = new Subject();
  openStartedRemissionLeavingDialog$ = new Subject();
  timerClear$ = new Subject();

  private remissionReminderActivate = false;
  constructor(
    private ssoService: SsoService,
    private store: Store,
    private userStateService: UserStateService,
    private meta: Meta,
    private route: ActivatedRoute,
    private http: HttpClient,
    public router: Router,
    private windowRef: WindowRef,
    private idle: Idle,
    private config: AppConfiguration
  ) {}

  public openStartedRemissionLeavingDialog() {
    this.openStartedRemissionLeavingDialog$.next();
  }

  public showLeavingRemissionModal() {
    this.leavingRemission$.next();
    this.registerNotIdleRemissionReminder();
  }

  public appLoadInitialisations() {
    // register smooth scroll polyfill
    smoothscroll.polyfill();
    // // check for token in queryparams
    this.getQueryParams()
      .pipe(takeUntil(this.destroy$))
      .subscribe((params: any) => {
        if (params && params['token'] && params['id_token']) {
          const token = params['token'];
          const id_token = params['id_token'];
          this.injectToken(token, id_token);
        }
        // register sso authentication
      });

    this.ssoService
      .registerAuthentication()
      .catch((error) => {
        console.error(error);
      })
      .then(() => {
        this.loadState();
        this.loadCountries();
        this.loadVats();
        this.loadSuppliers();
        this.shouldRemindForRemissionAtAppStart();
        this.registerIdleManager();
      });
  }

  getQueryParams() {
    return this.route.queryParams;
  }

  injectToken(token: string, id_token: string) {
    const claims = this.ssoService.parseJwt(token);
    const id_token_claims = this.ssoService.parseJwt(id_token);
    sessionStorage.setItem('access_token', token);
    sessionStorage.setItem('id_token', id_token);
    sessionStorage.setItem('granted_scopes', JSON.stringify(claims.scope));
    sessionStorage.setItem('id_token_claims_obj', JSON.stringify(id_token_claims));
  }

  registerNavigationEvents() {
    this.router.events
      .pipe(
        takeUntil(this.destroy$),
        filter((evt: any) => evt instanceof RoutesRecognized),
        pairwise()
      )
      .subscribe((events: RoutesRecognized[]) => {
        const currentUrl = events[1].urlAfterRedirects;
        this.sendNavigationEventsToGoogleAnalytics(currentUrl);
      });
  }

  registerNotIdleRemissionReminder() {
    this.registerTimedRemissionReminder(NOT_IDLE_REMISSION_REMINDER_MINUTES);
  }

  registerIdleRemissionReminder() {
    this.registerTimedRemissionReminder(IDLE_REMISSION_REMINDER_MINUTES);
  }

  private addMetas() {
    const zommingPreventionTag = <MetaDefinition>{
      name: 'viewport',
      content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no',
    };
    this.meta.addTag(zommingPreventionTag);
  }

  private loadCountries() {
    const countriesLoaded = this.store.selectSnapshot(CountrySelector.getCountries);
    if (Object.keys(countriesLoaded) && Object.keys(countriesLoaded).length === 0) {
      this.store.dispatch(new LoadCountries());
    }
  }

  private loadVats() {
    this.store.dispatch(new LoadVats());
  }

  private loadSuppliers() {
    this.store.dispatch(new LoadSuppliers());
  }

  private loadState() {
    const synced = this.store.selectSnapshot(AppState.isSynced);
    if (!synced) {
      this.store.dispatch(new LoadBranches());
      this.store.dispatch(new LoadUserBranch());
      this.userStateService
        .getUserState()
        .pipe(take(1))
        .subscribe((userData: any) => {
          this.store
            .dispatch(new ReloadSavedState(userData ? userData.content : '', true))
            .toPromise()
            .then(() => {
              this.loader$.next();
            });
        });
    }
  }

  private shouldRemindForRemissionAtAppStart() {
    this.store
      .select(RemissionSelectors.shouldRemindForRemission)
      .pipe(
        filter((data) => !isNullOrUndefined(data) && !this.remissionReminderActivate),
        take(1)
      )
      .subscribe((status) => {
        this.remissionReminderActivate = true;
        const shouldRemind = this.checkRemissionReminder(status);
        if (shouldRemind) {
          this.registerNotIdleRemissionReminder();
        }
      });
  }

  private checkRemissionReminder(status) {
    const isNotAtRemission = !this.router.url.includes('remission');
    (!!status && isNotAtRemission).ifTrue(() => {
      this.remissionReminder$.next();
      return true;
    });
    isNotAtRemission.ifFalse(() => {
      this.timerClear$.next();
    });
    return false;
  }

  private registerTimedRemissionReminder(minutes: number) {
    const emittingTime = 1000 * 60 * minutes;
    const timer$: Observable<number> = timer(emittingTime, emittingTime);
    timer$.pipe(takeUntil(this.timerClear$)).subscribe((interval) => {
      this.remissionReminder$.next();
    });
  }

  private registerIdleManager() {
    this.idle.setIdle(60);
    this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
    this.idle.onIdleStart.pipe(takeUntil(this.destroy$)).subscribe(() => {
      this.timerClear$.next();
      if (!!this.store.selectSnapshot(RemissionSelectors.shouldRemindForRemission) && !this.router.url.includes('remission')) {
        this.registerIdleRemissionReminder();
      }
    });
    this.idle.onIdleEnd.pipe(takeUntil(this.destroy$)).subscribe(() => {
      this.timerClear$.next();
      this.returToActiveStateReminder();
    });
    this.idle.watch();
  }

  private returToActiveStateReminder() {
    this.store
      .select(RemissionSelectors.shouldRemindForRemission)
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        take(1)
      )
      .subscribe((status) => {
        const isNotAtRemission = !this.router.url.includes('remission');
        (status && isNotAtRemission).ifTrue(() => {
          this.registerNotIdleRemissionReminder();
        });
        isNotAtRemission.ifFalse(() => {
          this.timerClear$.next();
        });
      });
  }

  public clearIdleReminder() {
    this.timerClear$.next();
  }

  private sendNavigationEventsToGoogleAnalytics(url: string) {
    if (this.config.includeGoogleAnalytics && typeof ga === 'function') {
      ga('set', 'page', url);
      ga('send', 'pageview');
    }
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.timerClear$.next();
  }

  public isIPadEnv() {
    const navigator = this.windowRef.nativeWindow.navigator as Navigator;
    const standalone = (navigator as any).standalone,
      userAgent = navigator.userAgent.toLowerCase(),
      ios = /iphone|ipod|ipad/.test(userAgent);
    return ios; // && !standalone;
  }
}
