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

import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbsService, DetailsComponent, MasterDetailViewComponent } from '@bci-web-core/core';
import { GenericLocationFormComponent, LocationMapService, LocationService, LsLocation } from '@bci-web-core/maps';
import { cloneDeep } from 'lodash';
import { Observable, of, Subject } from 'rxjs';
import {
  delay,
  distinctUntilChanged,
  filter,
  finalize,
  map,
  shareReplay,
  switchMap,
  takeUntil,
  tap,
} from 'rxjs/operators';

@Component({
  selector: 'bci-location-demo-form',
  templateUrl: './location-form.component.html',
  styleUrls: ['./location-form.component.scss'],
})
export class LocationFormComponent implements OnInit, OnDestroy, DetailsComponent {
  location: LsLocation;

  @ViewChild(GenericLocationFormComponent, { static: true })
  private locationForm: GenericLocationFormComponent;

  private ngUnsubscribe = new Subject<void>();

  constructor(
    protected masterDetailView: MasterDetailViewComponent,
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private locationService: LocationService,
    private locationMapService: LocationMapService,
    private breadcrumbService: BreadcrumbsService,
    private snackBar: MatSnackBar
  ) {}

  ngOnInit() {
    // deep linking
    this.activatedRoute.params
      .pipe(
        takeUntil(this.ngUnsubscribe),
        map((params) => params['id']),
        filter((id) => !!id),
        distinctUntilChanged(),
        tap(() => (this.location = undefined)), // reset form
        switchMap((id) => {
          // load location cancelling any running request
          if (id === 'new') {
            return of({ id: 'new', name: '', type: '' }).pipe(
              delay(0) // avoid change detection issues
            );
          } else {
            return this.locationService.querySingle(id);
          }
        })
      )
      .subscribe((location) => {
        if (!!location) {
          this.locationMapService.locationBeingEdited = this.location = cloneDeep(location);
          this.breadcrumbService.setTemporaryTitleForRoute(this.activatedRoute.snapshot, location.name);
          this.masterDetailView.openDetails();
        } else {
          this.leaveDetails();
        }
      });

    // register for geometry changes from map
    this.locationMapService.locationBeingEditedObservable
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((location) => this.locationForm.setGeometries(location));
  }

  ngOnDestroy() {
    this.closeDetails();
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  update(dirtyLocation: LsLocation) {
    this.locationMapService.locationBeingEdited = dirtyLocation;
  }

  /**
   * Save a location.
   * @param dirtyLocation LsLocation to save.
   */
  save(dirtyLocation: LsLocation) {
    if (dirtyLocation.id === 'new') {
      dirtyLocation.id = undefined;
    }
    this.locationService
      .upsert(dirtyLocation)
      .pipe(tap((result) => (this.location = result)))
      .subscribe(() => this.leaveDetails());
  }

  /**
   * Cancel editing the location.
   */
  cancel() {
    this.locationForm.reset();
    this.leaveDetails();
  }

  /**
   * Do not call directly, use navigation. This is used by routing guard.
   * @returns Whether details can be closed or not.
   */
  canCloseDetails(): boolean {
    if (this.locationForm.dirty) {
      this.snackBar.open('You have unsaved changes.', '', {
        duration: 5000,
        verticalPosition: 'top',
        horizontalPosition: 'center',
      } as MatSnackBarConfig);

      // enforce proper selection, e.g. when closing is triggered from deselection
      this.locationMapService.selectedLocations = [this.locationMapService.locationBeingEdited];
      return false;
    } else {
      return true;
    }
  }

  /**
   * Do not call directly, use navigation. This is used by routing guard.
   * @returns When the details have been closed.
   */
  closeDetails(): boolean | Observable<boolean> {
    const observable = this.masterDetailView.closeDetails().pipe(
      takeUntil(this.ngUnsubscribe),
      finalize(() => {
        // only deselect a single one, no deselection if non or multiple locations are selected
        if (this.locationMapService.selectedLocations.length === 1) {
          this.locationMapService.selectedLocations = [];
        }
        this.locationMapService.locationBeingEdited = null;
      }),
      shareReplay()
    );
    observable.subscribe();
    return observable;
  }

  private leaveDetails() {
    setTimeout(() => this.router.navigate(['..'], { relativeTo: this.activatedRoute }));
  }
}
