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

import { Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
import { BehaviorSubject, Observable } from 'rxjs';
import { NotificationItem, NotificationTypes } from './notification.models';

@Injectable({
  providedIn: 'root',
})
export class NotificationService {
  private readonly sessionStorageKey = 'webcore.core.notification.notifications';
  private readonly sessionNotifications: BehaviorSubject<NotificationItem[]> = new BehaviorSubject(
    this.getNotificationItemsFromSessionStorage()
  );
  private readonly portal = window['BCIPortal'];

  constructor(private matSnackBar: MatSnackBar) {}

  /**
   * Opens a BCI styled information notification
   * @param message
   * @param action (deprecated in portal context)
   * @param duration (deprecated in portal context)
   * @param messageDetails String to provide more message details (ONLY PORTAL)
   * @param link link to portal content (ONLY PORTAL)
   * @return MatSnackBarRef or undefined in portal context
   */
  success(
    message: string,
    action?: string,
    duration?: number,
    messageDetails?: string,
    link?: string
  ): MatSnackBarRef<SimpleSnackBar> {
    return this.notify(message, NotificationTypes.Success, action, duration, messageDetails, link);
  }

  /**
   * Opens a BCI styled warning notification
   * @param message
   * @param action (deprecated in portal context)
   * @param duration (deprecated in portal context)
   * @param messageDetails String to provide more message details (ONLY PORTAL)
   * @param link link to portal content (ONLY PORTAL)
   * @return MatSnackBarRef or undefined in portal context
   */
  warning(
    message: string,
    action?: string,
    duration?: number,
    messageDetails?: string,
    link?: string
  ): MatSnackBarRef<SimpleSnackBar> {
    return this.notify(message, NotificationTypes.Warning, action, duration, messageDetails, link);
  }

  /**
   * Opens a BCI styled error notification
   * @param message
   * @param action (deprecated in portal context)
   * @param duration (deprecated in portal context)
   * @param messageDetails String to provide more message details (ONLY PORTAL)
   * @param link link to portal content (ONLY PORTAL)
   * @return MatSnackBarRef or undefined in portal context
   */
  error(
    message: string,
    action?: string,
    duration?: number,
    messageDetails?: string,
    link?: string
  ): MatSnackBarRef<SimpleSnackBar> {
    return this.notify(message, NotificationTypes.Error, action, duration, messageDetails, link);
  }

  /**
   * Opens a BCI styled information notification
   * @param message
   * @param action (deprecated in portal context)
   * @param duration (deprecated in portal context)
   * @param messageDetails String to provide more message details (ONLY PORTAL)
   * @param link link to portal content (ONLY PORTAL)
   * @return MatSnackBarRef or undefined in portal context
   */
  information(
    message: string,
    action?: string,
    duration?: number,
    messageDetails?: string,
    link?: string
  ): MatSnackBarRef<SimpleSnackBar> {
    return this.notify(message, NotificationTypes.Information, action, duration, messageDetails, link);
  }

  /**
   * Returns all Notification of the current session
   */
  getSessionNotifications(): Observable<NotificationItem[]> {
    return this.sessionNotifications.asObservable();
  }

  /**
   * Deletes selected or all Notification of the current session
   * @param [selection] of NotificationItem to delete
   */
  deleteSessionNotifications(selection?: Array<NotificationItem>): void {
    if (selection && selection.length > 0) {
      const sessionItems = this.getNotificationItemsFromSessionStorage().filter(
        (item) => selection.map((sec) => JSON.stringify(sec)).indexOf(JSON.stringify(item)) === -1
      );
      sessionStorage.setItem(this.sessionStorageKey, JSON.stringify(sessionItems));
      return this.sessionNotifications.next(sessionItems);
    }
    sessionStorage.removeItem(this.sessionStorageKey);
    this.sessionNotifications.next([]);
  }

  /**
   * Adds a NotificationItem to the current session
   * @param notificationItem
   */
  private addNotificationItemToCurrentSession(notificationItem: NotificationItem): void {
    const sessionItems = this.getNotificationItemsFromSessionStorage();
    sessionItems.push(notificationItem);
    sessionStorage.setItem(this.sessionStorageKey, JSON.stringify(sessionItems)); // only string values allowed https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem#Parameters
    this.sessionNotifications.next(sessionItems);
  }

  /**
   * Returns the NotificationItem array directly from SessionStorage
   */
  private getNotificationItemsFromSessionStorage(): NotificationItem[] {
    // TODO: Replace with storage service, when available
    const rawValue = sessionStorage.getItem(this.sessionStorageKey);
    if (rawValue) {
      return JSON.parse(rawValue) as NotificationItem[];
    } else {
      return [];
    }
  }

  /**
   * Opens a BCI-Styled Toast-Notification
   * Adds notification to sessionStorage
   * Emits NotificationItem to (deprecated) subject
   * @param message Message to display
   * @param action Button title (optional, default: no action)
   * @param notificationType Notification Style (optional, default: NotificationTypes.information)
   * @param duration Duration of the notification (optional, default: 2000ms)
   * @param messageDetails String to provide more message details (ONLY PORTAL)
   * @param link link to portal content (ONLY PORTAL)
   */
  private notify(
    message: string,
    notificationType: NotificationTypes = NotificationTypes.Information,
    action?: string,
    duration = 3000,
    messageDetails?: string,
    link?: string
  ): MatSnackBarRef<SimpleSnackBar> | undefined {
    this.addNotificationItemToCurrentSession(
      new NotificationItem(message, action, notificationType, new Date(), messageDetails, link)
    );

    if (typeof this.portal !== 'undefined' && typeof this.portal.dispatchEvent === 'function') {
      this.portal.dispatchEvent(notificationType, 'bci.portal.message.created', message, messageDetails, link);
      return undefined;
    }

    return this.matSnackBar.open(message, action, {
      duration: !action && duration,
      horizontalPosition: 'center',
      verticalPosition: 'bottom',
      panelClass: notificationType,
    });
  }
}
