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

import { Location } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ModalWindowService } from '@bci-web-core/core';
import {
  Category,
  CategoryService,
  PinboardComponent,
  PinboardConfig,
  PinboardGridComponent,
  UserSettingChangeEvent,
  Widget,
  WidgetContentComponent,
  WidgetDefinition,
  WidgetFilter,
  WidgetSettingsComponent,
  WidgetTypes,
} from '@bci-web-core/pinboard';
import {
  ChartjsContentComponent,
  ChartjsWidgetSetting,
  TextContentComponent,
  TextWidgetUserSetting,
} from '@bci-web-core/pinboard-widgets';
import * as _ from 'lodash';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { DashboardService } from '../services/dashboard.service';
import { MetricSettingsComponent } from '../widget-settings/metric-settings/metric-settings.component';
import { MyOwnFormComponent } from '../widget-settings/my-own-form/my-own-form.component';
import { MetricsComponent, MetricsSetting, MetricsUserSetting } from '../widgets/metrics/metrics.component';
import {
  MyOwnWidgetContentComponent,
  MyOwnWidgetUserSetting,
} from '../widgets/my-own-widget-content/my-own-widget-content.component';
import { DashboardSettingsComponent } from './../dashboard-settings/dashboard-settings.component';

@Component({
  selector: 'bci-demo',
  templateUrl: './demo.component.html',
  styleUrls: ['./demo.component.scss'],
})
export class DemoComponent implements OnInit {
  dashboardId: string;
  dashboards: PinboardConfig[] = [];
  isLoading: boolean;
  searchTerm: string;
  selectValue = 'abc';
  readOnly = true;
  @ViewChild(PinboardComponent) private pinboard: PinboardComponent;
  @ViewChild(PinboardGridComponent) private pinboardGrid: PinboardGridComponent;
  @ViewChild(WidgetSettingsComponent, { static: true }) private widgetSettings: WidgetSettingsComponent;

  constructor(
    public dashboardService: DashboardService,
    private categoryService: CategoryService,
    private http: HttpClient,
    private location: Location,
    private modalWindowService: ModalWindowService
  ) {
    this.addCustomWidgetComponent();
  }

  ngOnInit() {
    this.isLoading = true;

    /**
     * Add categories to pinboard
     */
    this.getCategories().subscribe((cats: Category[]) => {
      for (const cat of cats) {
        this.categoryService.addCategory(cat);
      }
      console.log('REGISTERED CATEGORIES:', this.categoryService.getCategories());
    });

    /**
     * Add definitions to pinboard
     */
    this.getWidgetDefinitions().subscribe((defs: WidgetDefinition<WidgetContentComponent, any, any>[]) => {
      for (const def of defs) {
        this.pinboard.defineWidget(def);
      }
      console.log('DEFINED WIDGETS:', this.pinboard.getDefinedWidgets());
    });

    // Gets all dashboard tabs
    this.dashboardService.getAllDashboardHeaders().subscribe(
      (res: PinboardConfig[]) => {
        if (res && res.length > 0) {
          console.log('DASHBOARDS LOADED:', res);
          this.dashboards = res;
        } else {
          // If no dashboards have been found for a user, create one
          this.dashboardService.createDashboard(null).subscribe((dashboard: PinboardConfig) => {
            this.dashboardId = dashboard.id;
            console.log('DASHBOARD CREATED:', dashboard);
            this.dashboards = [dashboard];
          });
        }
      },
      /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
      (err) => {
        this.isLoading = false;
      }
    );
  }

  /**
   * This method is just to demonstrate, that you could react to setting changes
   */
  public settingChanged(changeEvent: UserSettingChangeEvent): void {
    console.log('SETTING CHANGED:', changeEvent);
  }

  /**
   * This method is for development/testing only!
   */
  public test(): void {
    console.log(this.pinboard.getDefinedWidgets());
  }

  /**
   * Event when a user clicks 'save' in the widget settings form
   */
  public saveWidget(widget: Widget): void {
    // Here you can post the widget to your server
    console.log('SAVED WIDGET:', widget);
    this.dashboardService.updateWidget(widget).subscribe();
  }

  /**
   * Loads the pinboard from the local storage or database
   */
  public loadPinboard(): void {
    setTimeout(() => {
      this.dashboardService.loadUserConfiguration(this.dashboardId).subscribe(
        (conf: PinboardConfig) => {
          console.log('LOAD PINBOARD CONFIG:', conf);
          this.isLoading = false;
          this.pinboard.loadPinboardConfig(conf);
        },
        /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
        (err) => {
          this.isLoading = false;
        }
      );
    }, 500);
  }

  get widgetFilters(): Array<WidgetFilter> {
    return [
      { key: 'searchTerm', value: this.searchTerm },
      { key: 'select', value: this.selectValue },
    ];
  }

  /**
   * Opens dashboard settings when user clicks on the geat icon in the header
   */
  openDashboardSettingsDialog() {
    const dialogRef = this.modalWindowService.openDialogWithComponent(DashboardSettingsComponent, {
      data: { dashboards: this.dashboards },
      panelClass: 'dashboard-settings-popup',
    });
    dialogRef.afterClosed().subscribe((response) => this.saveDashboardSettings(response));
  }

  /**
   * Opens a specific dashboard
   */
  openDashboard(dashboard: PinboardConfig) {
    this.dashboardId = dashboard.id;
    this.isLoading = true;
    this.loadPinboard();
  }

  /**
   * Logs the export result
   */
  public exportPinboard(): void {
    const conf = this.pinboard.exportPinboardConfig('someId', 'someLabel');
    console.log('EXPORT PINBOARD CONFIG:', conf);
  }

  /**
   * Delete widget in non edit mode
   */
  public widgetRemoved(widget: Widget): void {
    const selectedDashboard: PinboardConfig = this.dashboards.find((dashboard) => dashboard.id === this.dashboardId);
    if (selectedDashboard) {
      selectedDashboard.settings = selectedDashboard.settings.filter(dashboardWidget => dashboardWidget.id !== widget.id);
      this.dashboardService.saveDashboard(selectedDashboard).subscribe();
    }
  }

  /**
   * Called when the user updates the dashboard labels or order
   */
  private saveDashboardSettings(dashboards: any) {
    if (!dashboards) {
      return;
    }
    this.isLoading = true;
    const originalDashboards = _.cloneDeep(this.dashboards);
    this.dashboardService
      .updateAllDashboards(dashboards.created, dashboards.updated, dashboards.deleted)
      .subscribe((res) => {
        if (res && res.length > 0) {
          // Switch to the highest priority newly created dashboard
          const createdDashboard = this.getCreatedDashboard(originalDashboards, res);
          if (createdDashboard) {
            this.pinboard.activeDashboard = createdDashboard;
          }
          this.dashboards = res;
        }
        this.isLoading = false;
      });
  }

  /**
   * Gets the highest priority newly created dashboard
   */
  private getCreatedDashboard(originalDashboards: PinboardConfig[], newDashboards: PinboardConfig[]) {
    const createdDashboards = _.differenceWith(
      newDashboards,
      originalDashboards,
      (a: PinboardConfig, b: PinboardConfig) => {
        return a.id === b.id;
      }
    );
    if (createdDashboards.length > 0) {
      return createdDashboards[0];
    }
  }

  /**
   * If you want to add your custom widget content, you need to define it first
   */
  private addCustomWidgetComponent(): void {
    WidgetTypes.push(
      {
        widgetType: 'ChartjsWidget',
        contentComponentType: ChartjsContentComponent,
        widgetSettingType: ChartjsWidgetSetting,
      },
      {
        widgetType: 'TextWidget',
        contentComponentType: TextContentComponent,
        widgetUserSettingType: TextWidgetUserSetting,
      }
    );

    WidgetTypes.push({
      widgetType: 'MyOwnWidget',
      contentComponentType: MyOwnWidgetContentComponent,
      widgetUserSettingType: MyOwnWidgetUserSetting,
      widgetSettingType: MyOwnWidgetUserSetting,
      customSettingFormType: MyOwnFormComponent,
    });

    WidgetTypes.push({
      widgetType: 'Metrics',
      contentComponentType: MetricsComponent,
      widgetUserSettingType: MetricsUserSetting,
      widgetSettingType: MetricsSetting,
      customSettingFormType: MetricSettingsComponent,
    });
  }

  /**
   * Requests mock categories
   */
  private getCategories(): Observable<Category[]> {
    const categoryUrl = this.location.prepareExternalUrl('/assets/mocks/categories.json');
    return this.http.get<Category[]>(categoryUrl);
  }

  /**
   * Requests mock definitions
   */
  private getWidgetDefinitions(): Observable<WidgetDefinition<WidgetContentComponent, any, any>[]> {
    const mockUrl = this.location.prepareExternalUrl('/assets/mocks/widget-definitions.json');
    return this.http.get<WidgetDefinition<WidgetContentComponent, any, any>[]>(mockUrl).pipe(
      map((defs) => {
        const instances = [];
        defs.forEach((def) => {
          instances.push(new WidgetDefinition<WidgetContentComponent, any, any>(def));
        });
        return instances;
      })
    );
  }
}
