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

import { Injectable, Type } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { identity, Observable, of } from 'rxjs';

import { defaultIfEmpty, filter, map, mergeMap } from 'rxjs/operators';
import { DetailsComponent } from './details-component';

@Injectable()
export class CloseDetailsGuard  {
  canDeactivate(
    component: DetailsComponent,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    if (component) {
      return this.canDeactivateDetailsComponent(<DetailsComponent>component, nextState, currentState);
    } else {
      return false;
    }
  }

  protected asObservable<T>(value: T | Observable<T>): Observable<T> {
    if (value instanceof Observable) {
      return value;
    }
    return of(value);
  }

  protected areDetailRoutesWithSameMaster(currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) {
    if (!nextState) {
      return false;
    }

    let currentChild = currentState.root.firstChild;
    let nextChild = nextState.root.firstChild;
    while (!!currentChild && !!nextChild) {
      if (this.isDetailsComponentType(currentChild.component)) {
        if (!this.isDetailsComponentType(nextChild.component)) {
          return false;
        } else {
          return currentChild.parent.component === nextChild.parent.component;
        }
      } else {
        currentChild = currentChild.firstChild;
        nextChild = nextChild.firstChild;
      }
    }
    return false;
  }

  protected isDetailsComponentType(componentType: Type<any> | string | null) {
    if (componentType instanceof Type) {
      return this.isDetailsComponent(componentType.prototype);
    }
    return false;
  }

  protected isDetailsComponent(component: any): component is DetailsComponent {
    return component.canCloseDetails !== undefined;
  }

  private canDeactivateDetailsComponent(
    component: DetailsComponent,
    nextState: RouterStateSnapshot,
    currentState: RouterStateSnapshot
  ) {
    return this.asObservable(component.canCloseDetails()).pipe(
      filter(identity),
      mergeMap(() =>
        this.areDetailRoutesWithSameMaster(currentState, nextState)
          ? of(true)
          : this.asObservable(component.closeDetails()).pipe(map(() => true))
      ),
      defaultIfEmpty(false)
    );
  }
}
