import { Inject, Injectable, LOCALE_ID } from '@angular/core';
import { IPanelConfigModel } from './models/panel-config.model';
import { HttpClient } from '@angular/common/http';
import { IAppConfigModel } from './models/app-config.model';
import { NavigationEnd, RouteConfigLoadStart, Router } from '@angular/router';
import { environment } from '@environments/environment';
import { AuthService } from '@shared/auth/auth.service';
import { FleioId } from '@fleio-api/base-model/base-fleio-object.model';
import { BehaviorSubject, Observable } from 'rxjs';

const configFilesPath = 'assets/config/';
const configBaseFilesPath = 'assets/config-base/';

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  private isSubscribedToRouterEvents = false;

  private panels: { [panelName: string]: IPanelConfigModel } = {};

  private currentUrl: string;
  private previousUrl: string = null;

  public app: IAppConfigModel;

  // easy access shortcuts for a specific panel configuration
  public enduser: IPanelConfigModel;
  public staff: IPanelConfigModel;
  public current: IPanelConfigModel;
  public currentConfigurationName: string;

  // auth service
  public authService: AuthService = null;
  activeClientId: FleioId;

  private activeClientChangedBS = new BehaviorSubject<FleioId>(null);
  activeClientChanged$ = this.activeClientChangedBS.asObservable();
  private pageChanged: boolean;

  public appBreakpointObservable: Observable<string>;


  constructor(
    private httpClient: HttpClient,
    @Inject(LOCALE_ID) public locale: string
  ) {
  }

  private _initShortcuts(): void {
    if (this.panels.hasOwnProperty('enduser')) {
      this.enduser = this.panels.enduser;
    }
    if (this.panels.hasOwnProperty('staff')) {
      this.staff = this.panels.staff;
    }
  }

  private setActiveConfiguration(configurationName: string) {
    // update configuration
    this.current = this.panels[configurationName];
    this.currentConfigurationName = configurationName;
  }

  public setActiveUrl(url: string) {
    // update urls
    this.previousUrl = this.currentUrl;
    if (window.location.hash && url.indexOf('#') === -1) {
      this.currentUrl = `${url}${window.location.hash}`;
    } else {
      this.currentUrl = url;
    }
  }

  public subscribeToRouterEvents(router: Router) {
    if (!this.isSubscribedToRouterEvents) {
      router.events.subscribe(event => {
        if (event instanceof RouteConfigLoadStart) {
          const routeConfigLoadStart = event as RouteConfigLoadStart;
          if (routeConfigLoadStart.route.data && routeConfigLoadStart.route.data.configurationName) {
            this.setActiveConfiguration(
              routeConfigLoadStart.route.data.configurationName
            );
          }
        }

        if (event instanceof NavigationEnd) {
          let newUrl = event.url;
          if (window.location.hash) {
            newUrl += window.location.hash;
          }
          if (newUrl !== this.currentUrl) {
            this.pageChanged = true;
          }
          this.setActiveUrl(event.url);
        }
      });
      this.isSubscribedToRouterEvents = true;
    }
  }

  public hasPageChanged(): boolean {
    return this.pageChanged;
  }

  public resetPageChanged() {
    this.pageChanged = false;
  }

  public getDefaultRefreshInterval(short: boolean = false): number {
    if (this.current && this.current.settings && this.current.settings.refreshIntervals) {
      if (short) {
        return this.current.settings.refreshIntervals.defaultIntervalShort || 5000;
      } else {
        return this.current.settings.refreshIntervals.defaultInterval || 10000;
      }
    }

    return short ? 5000 : 10000;
  }

  public getPanelApiUrl(endpoint: string): string {
    if (this.current) {
      if (!endpoint) {
        // return api url but without the last slash, like we would do if we had an endpoint
        return this.current.urls.backendApiUrl.substring(0, this.current.urls.backendApiUrl.length - 1);
      }
      return this.current.urls.backendApiUrl + endpoint;
    } else {
      if (environment.enableErrorLogging) {
        console.error(`Cannot determine api url for endpoint ${endpoint}`);
      }
      return '';
    }
  }

  public getPanelUrl(fragment: string, objectId?: FleioId): string {
    if (fragment === this.getPanelHomeName()) {
      // is only called like this from isMenuActive
      return this.getPanelHomeUrl();
    }
    const clientIdFragment = this.activeClientId ? `/${this.activeClientId}` : '';
    if (objectId) {
      fragment = `${fragment}/${objectId}`;
    }
    if (fragment) {
      fragment = '/' + fragment;
    }
    if (this.panelIsRoot()) {
      return clientIdFragment + fragment;
    }
    return this.getPanelHomeUrl() + clientIdFragment + fragment;
  }

  public getPanelHomeUrl(): string {
    const clientIdFragment = this.activeClientId ? `/${this.activeClientId}` : '';
    if (this.panelIsRoot()) {
      return clientIdFragment ? clientIdFragment : '/';
    }
    return '/' + this.getPanelHomeName() + clientIdFragment;
  }

  public panelIsRoot(): boolean {
    return this.getPanelHomeName() === environment.basePanel;
  }

  public getPanelHomeName(): string {
    if (this.current && this.current.urls) {
      return this.current.urls.homeName;
    } else {
      if (environment.enableErrorLogging) {
        console.error('Cannot get panel home name');
      }
      return '';
    }
  }

  public getImagePath(imageName: string): string {
    if (this.current && this.current.settings) {
      return this.current.settings.imagesPath + imageName;
    }
    return '';
  }

  public hasPrevUrl() {
    return this.previousUrl !== null &&
      (typeof this.previousUrl !== 'undefined') && this.previousUrl !== this.currentUrl;
  }

  public hasValidPrevUrl(): boolean {
    if (this.hasPrevUrl()) {
      const invalidUrlSegments = [
        '/disconnected', '/sfa/google-authenticator/confirm', '/sfa/sms-authenticator/confirm'
      ];
      return !invalidUrlSegments.find(invalidUrlSegment => this.previousUrl.indexOf(invalidUrlSegment) > -1);
    } else {
      return false;
    }
  }

  public getPrevUrl(defaultFragment: string = null): string {
    if (this.hasValidPrevUrl()) {
      return this.previousUrl;
    } else {
      return defaultFragment === null ? this.getPanelHomeUrl() : this.getPanelUrl(defaultFragment);
    }
  }

  public getCurrentUrl(includeFragment: boolean = true): string {
    if (includeFragment) {
      return this.currentUrl;
    } else {
      return this.currentUrl.split('#')[0];
    }
  }

  checkNextUrl(nextUrl: string): string {
    // remove next url if it references home page
    return nextUrl === this.getPanelHomeUrl() ? null : nextUrl;
  }

  public getDashboardVersion(): string {
    if (this.current && this.current.settings && this.current.settings.dashboardVersion) {
      return this.current.settings.dashboardVersion;
    }
    return '';
  }

  isDict(value: any): boolean {
    return typeof value === 'object' && value !== null && !(value instanceof Array) && !(value instanceof Date);
  }

  mergeObjects<T>(a: T, b: T): T {
    for (const key in a) {
      if (a.hasOwnProperty(key) && this.isDict(a[key])) {
        if (b.hasOwnProperty(key) && this.isDict(b[key])) {
          b[key] = this.mergeObjects(a[key], b[key]);
        }
      }
    }
    return Object.assign({}, a, b) as T;
  }

  loadPanelConfiguration(panelName: string, fileName: string): Promise<void> {
    return new Promise<void>(
      (resolve, reject) => {
        const panelConfigFile = configFilesPath + fileName;
        const panelBaseConfigFile = configBaseFilesPath + fileName;
        this.httpClient.get(panelBaseConfigFile).toPromise().then(
          (baseResponse: IPanelConfigModel) => {
            this.httpClient.get(panelConfigFile).toPromise().then(
              (response: IPanelConfigModel) => {
                this.panels[panelName] = this.mergeObjects<IPanelConfigModel>(baseResponse, response);
                resolve();
              }
            ).catch(
              () => {
                reject('Failed to load configuration file ${fileName} for panel ${panelName}');
              }
            );
          }
        ).catch(
          () => {
            reject('Failed to load base configuration file ${fileName} for panel ${panelName}');
          }
        );
      }
    );
  }

  load() {
    return new Promise<void>((resolve, reject) => {
      const promises = [];
      Object.keys(environment.panels).map(panelName => {
        const panel = environment.panels[panelName];
        promises.push(this.loadPanelConfiguration(panelName, panel.configFile));
      });
      Promise.all(promises).then(
        () => {
          this._initShortcuts();
          resolve();
        }
      ).catch(
        () => {
          reject('Failed to load panel configurations');
        }
      );
    });
  }

  setActiveClientId(activeClientId: FleioId) {
    const newActiveClientId = Number.isInteger(activeClientId) ? activeClientId.toString() : activeClientId;
    if (newActiveClientId !== this.activeClientId) {
      this.activeClientId = newActiveClientId;
      this.activeClientChangedBS.next(this.activeClientId);
    }
  }

  refreshActiveClient() {
    this.activeClientChangedBS.next(this.activeClientId);
  }
}
