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

import { BreakpointObserver } from '@angular/cdk/layout';
import { Location, LocationStrategy } from '@angular/common';
import { Injectable } from '@angular/core';
import { NavigationExtras, Router } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { BOSCH_BREAKPOINTS } from '../../core-configuration/bosch-breakpoints';
import { MediaQueryBuilder } from '../media-query-builder';

@Injectable({
  providedIn: 'root',
})
export class BciSidebarService {
  /**
   * Define media query for sidebar to be expanded.
   * Must be set before first call to getSidebarState to have an effect.
   */
  sidebarExpansionMediaQuery = MediaQueryBuilder.is().gt(BOSCH_BREAKPOINTS.M).query;

  private sidebarState$: BehaviorSubject<boolean>;
  private sidebarVisibility$ = new BehaviorSubject<boolean>(true);

  constructor(
    private router: Router,
    private location: Location,
    private locationStrategy: LocationStrategy,
    private breakpointObserver: BreakpointObserver
  ) {}

  public setSidebarState(open: boolean) {
    this.initAndGetSidebarStateBehavior().next(open);
    if (typeof window['BCIPortal'] !== 'undefined') {
      window.dispatchEvent(new CustomEvent('bci.portal.navigation.toggle', { detail: { closed: !open } }));
    }
  }

  public setSidebarVisibility(visible: boolean) {
    this.sidebarVisibility$.next(visible);
  }

  public getSidebarState(): Observable<boolean> {
    return this.initAndGetSidebarStateBehavior().asObservable();
  }

  get isSidebarOpen() {
    return this.initAndGetSidebarStateBehavior().getValue();
  }

  public getSidebarVisibility(): Observable<boolean> {
    return this.sidebarVisibility$.asObservable();
  }

  private initAndGetSidebarStateBehavior(): BehaviorSubject<boolean> {
    if (!this.sidebarState$) {
      this.sidebarState$ = new BehaviorSubject<boolean>(
        this.breakpointObserver.isMatched(this.sidebarExpansionMediaQuery)
      );
      this.breakpointObserver
        .observe(this.sidebarExpansionMediaQuery)
        .subscribe((result) => this.setSidebarState(result.matches));
    }
    return this.sidebarState$;
  }

  /**
   * Navigates to the URL either internally (with Angular Router) or externally (window.location).
   * If there is not http protocol or if the origin is the same and the route is part of the router config, navigate internally.
   * Else, navigate externally.
   * @param url Absolute URL or internal route.
   * @param extras {@link NavigationExtras}
   * @returns The {@link router} navigate return
   */
  public navigateToUrl(url: string, extras?: NavigationExtras): Promise<boolean> {
    let urlObject;
    try {
      urlObject = new URL(url);
    } catch (e) {
      // assuming TypeError, Invalid URL -> navigate internally directly to URL
      return this.router.navigate([url], extras);
    }
    if (!!urlObject) {
      let baseHref = this.locationStrategy.getBaseHref();
      baseHref = baseHref.endsWith('/') ? baseHref : `${baseHref}/`;
      if (urlObject.origin === window.location.origin && urlObject.pathname.startsWith(baseHref)) {
        return this.router.navigate([urlObject.pathname], extras);
      } else {
        window.location.href = urlObject.href;
      }
    }
  }
}
