import {
  VuexModule,
  getModule,
  Mutation,
  Module,
  Action,
} from 'vuex-module-decorators'
import { Store } from 'vuex'
import { TypesCount } from '../types/TypesCount'
import { NotificationEvent } from '../types/NotificationEvent'
import NotificationService from '../services/NotificationService'
import LocalStorageHelper from '../helpers/LocalStorageHelper'

@Module({
  name: 'NotificationsStore',
  namespaced: true,
})
export default class NotificationsStore extends VuexModule {
  clientUid: string = ''
  isLoading = false
  notifications: NotificationEvent[] = []
  popupNotifications: NotificationEvent[] = []
  totalNotificationsCount = 0
  currentType: string | null = null
  typesCount: TypesCount = {}
  excludedTypes: string[] = []
  excludedPopupTypes: string[] = []

  get types(): string[] {
    return Object.keys(this.typesCount)
  }

  get allowedTypes() {
    return this.types.filter(
      type => !this.excludedTypes.includes(type) && this.typesCount[type] > 0
    )
  }

  get allNotificationsCount(): number {
    return this.allowedTypes.reduce(
      (count, type) => count + this.typesCount[type],
      0
    )
  }

  get currentTypeNotificationCount(): number {
    return this.currentType === null
      ? this.allNotificationsCount
      : this.typesCount[this.currentType]
  }

  get filteredNotifications() {
    if (this.currentType === null) {
      return this.notifications.filter(
        notification => !this.excludedTypes.includes(notification.type)
      )
    }

    return this.notifications.filter(n => n.type === this.currentType)
  }

  @Mutation
  toggleExcludeType(type: string) {
    if (this.excludedTypes.includes(type)) {
      this.excludedTypes.splice(this.excludedTypes.indexOf(type), 1)
    } else {
      if (!this.excludedPopupTypes.includes(type)) {
        this.excludedPopupTypes.push(type)
      }

      this.excludedTypes.push(type)
    }

    LocalStorageHelper.saveExcludedTypes(this.excludedTypes)
  }

  @Mutation
  toggleExcludePopupType(type: string) {
    if (this.excludedPopupTypes.includes(type)) {
      this.excludedPopupTypes.splice(this.excludedPopupTypes.indexOf(type), 1)

      if (this.excludedTypes.includes(type)) {
        this.excludedTypes.splice(this.excludedTypes.indexOf(type), 1)
      }
    } else {
      this.excludedPopupTypes.push(type)
    }

    LocalStorageHelper.saveExcludedTypes(this.excludedTypes)
    LocalStorageHelper.saveExcludedPopupTypes(this.excludedPopupTypes)
  }

  @Mutation
  setExcludedTypes(types: string[]) {
    this.excludedTypes = types
  }

  @Mutation
  setExcludedPopupTypes(types: string[]) {
    this.excludedPopupTypes = types
  }

  @Mutation
  setClientUid(value: string) {
    this.clientUid = value
  }

  @Mutation
  setTypesCount(typesCount: TypesCount) {
    this.typesCount = typesCount

    const currentCount = this.currentType
      ? this.typesCount[this.currentType]
      : 0

    if (currentCount < 1) {
      this.currentType = null
    }
  }

  @Mutation
  setCurrentType(type: string | null) {
    this.currentType = type
    this.notifications = []
  }

  @Mutation
  setNotifications(notifications: NotificationEvent[]) {
    this.notifications = notifications
  }

  @Mutation
  setTotalNotificationsCount(value: number) {
    this.totalNotificationsCount = value
  }

  @Mutation
  setIsLoading(value: boolean) {
    this.isLoading = value
  }

  @Mutation
  addNotification(notification: NotificationEvent) {
    this.notifications.unshift(notification)
    this.typesCount[notification.type]++
    this.totalNotificationsCount++
    if (!this.excludedPopupTypes.includes(notification.type)) {
      this.popupNotifications.unshift(notification)
    }
  }

  @Mutation
  removePopupNotification(notificationId: number) {
    const index = this.popupNotifications.findIndex(
      notification => notification.id === notificationId
    )

    if (index !== -1) {
      this.popupNotifications.splice(index, 1)
    }
  }

  @Mutation
  deleteNotification(notificationId: number) {
    const index = this.notifications.findIndex(
      notification => notification.id === notificationId
    )

    if (index !== -1) {
      const closedNotification = this.notifications.splice(index, 1)[0]
      const countInType = this.typesCount[closedNotification.type]

      this.totalNotificationsCount--
      this.typesCount[closedNotification.type] = countInType - 1
    }
  }

  @Mutation
  clearNotifications() {
    this.notifications = []
    this.currentType = null
    this.typesCount = {}
    this.totalNotificationsCount = 0
  }

  @Action({ rawError: true })
  loadTypesCount() {
    return NotificationService.loadTypes().then(typesCount =>
      this.setTypesCount(typesCount)
    )
  }

  @Action({ rawError: true })
  loadNotifications() {
    this.setIsLoading(true)

    return NotificationService.getNotifications({
      type: this.currentType || '',
      excludedTypes: this.excludedTypes,
    }).then(payload => {
      this.setNotifications(payload.notifications)
      this.setTotalNotificationsCount(payload.count)
      this.setIsLoading(false)
    })
  }

  @Action({ rawError: true })
  loadNotificationsOnScroll() {
    return NotificationService.getNotifications({
      count: this.notifications.length,
      type: this.currentType || '',
      excludedTypes: this.excludedTypes,
    }).then(payload => {
      this.setNotifications([...this.notifications, ...payload.notifications])
      this.setTotalNotificationsCount(payload.count)
    })
  }

  @Action({ rawError: true })
  closeNotification(notificationId: number) {
    this.deleteNotification(notificationId)
    this.removePopupNotification(notificationId)

    return NotificationService.closeNotification(notificationId)
  }

  @Action({ rawError: true })
  closeAllNotifications() {
    return NotificationService.closeAllNotifications().then(() =>
      this.clearNotifications()
    )
  }
}

export function getNotificationsStore(store: Store<any>) {
  return getModule(NotificationsStore, store)
}
