import { Injectable, NgZone } from '@angular/core';
import {
  ActivatedRoute,
  NavigationCancel,
  NavigationEnd,
  NavigationError,
  NavigationExtras,
  NavigationStart,
  Router
} from '@angular/router';
import { RouteHelper } from './route-helper';
import { DEFAULT_BOOST_INTERVALS, RefreshTimer } from './helpers/refresh-timer';
import { ConfigService } from '@shared/config/config.service';

@Injectable({
  providedIn: 'root'
})
export class RefreshService {
  private refreshTimer: RefreshTimer;
  private refreshing = false;
  private refreshSuspended = false;

  constructor(private router: Router, private route: ActivatedRoute, private configService: ConfigService,
              private ngZone: NgZone) {
    router.events.subscribe((event) => {
      switch (event.constructor.name) {
        case NavigationStart.name:
          this.refreshSuspended = true;
          break;

        case NavigationCancel.name:
        case NavigationError.name:
        case NavigationEnd.name:
          this.refreshSuspended = false;
          break;

        default:
          break;
      }
    });

    this.refreshTimer = new RefreshTimer(this.configService.getDefaultRefreshInterval(), () => {
      this.refresh();
    }, this.configService, this.ngZone, null, false);
  }

  public executeIfNotPageChanged(callback: () => void) {
    if (!this.configService.hasPageChanged()) {
      callback();
    }
  }

  public resetPageChanged() {
    this.configService.resetPageChanged();
  }

  public refresh(callback?: () => void, ifPageNotChanged: boolean = false) {
    this.ngZone.run(() => {
      if (!this.refreshing && !this.refreshSuspended) {
        const routeHelper = new RouteHelper(this.route);
        const routeToRefresh = routeHelper.getFinalRoute();
        if (routeToRefresh.snapshot.routeConfig.runGuardsAndResolvers !== 'always') {
          console.error(`runGuardsAndResolvers is not configured properly for refreshed route: ${routeHelper.getRoutePath()}`);
        } else {
          const refreshCallback = () => {
            this.refreshing = true;
            this.router.navigate([], {
              relativeTo: routeToRefresh,
              queryParams: routeToRefresh.snapshot.queryParams,
              skipLocationChange: true,
            }).then(() => {
              this.refreshing = false;
              if (callback) {
                callback();
              }
            });
          };
          if (ifPageNotChanged) {
            this.executeIfNotPageChanged(refreshCallback);
          } else {
            refreshCallback();
          }
        }
      }
    });
  }

  public redirect(
    url: string,
    propagateParams: string[] = [],
    ifPageNotChanged: boolean = false,
    queryParams: Record<string, any> = null,
    fragment: string = null,
  ) {
    const navigationExtras: NavigationExtras = {
      queryParams: RouteHelper.getQueryParams(this.router, propagateParams),
      queryParamsHandling: 'merge',
    };
    if (fragment) {
      navigationExtras.fragment = fragment;
    }
    if (queryParams) {
      Object.assign(navigationExtras.queryParams, queryParams);
    }
    const redirectCallback = () => {
      this.router.navigate([url], navigationExtras).catch(() => {
      });
    };

    if (ifPageNotChanged) {
      this.executeIfNotPageChanged(redirectCallback);
    } else {
      redirectCallback();
    }
  }

  startRefreshTimer(interval: number) {
    this.refreshTimer.stop();

    this.refreshSuspended = false;
    this.refreshing = false;

    this.refreshTimer.setInterval(interval, true);
  }

  stopRefreshTimer() {
    this.refreshTimer.stop();
  }

  boost(boostIntervals: number[] = DEFAULT_BOOST_INTERVALS) {
    this.refreshTimer.boost(boostIntervals);
  }
}
