import { VuexModule, Mutation, Module, Action } from 'vuex-module-decorators'
import { TypesCount } from '../types/TypesCount'
import { NotifyHelper } from '@/helpers'
import { NotificationCode, NotificationEvent } from '../types/NotificationEvent'
import LocalStorageHelper from '../helpers/LocalStorageHelper'
import NotificationService from '@/widgets/notifications/http/services/NotificationService'
import AuthService from '@/widgets/notifications/http/services/AuthService'
import NotificationWebsocketService from '@/widgets/notifications/http/services/NotificationWebsocketService'

@Module({
  name: 'NotificationsModule',
  namespaced: true,
})
export default class NotificationsModule extends VuexModule {
  clientUid: string = ''
  isLoading = false
  notifications: NotificationEvent[] = []
  popupNotifications: NotificationEvent[] = []
  currentCodes: NotificationCode[] = []
  typesCount: TypesCount = {}
  excludedCodes: string[] = []
  excludedPopupCodes: string[] = []
  isInitialisedWebsocket = false

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

  get getCountByCode() {
    return (code: NotificationCode): number => this.typesCount[code]?.count || 0
  }

  get totalCount() {
    return Object.keys(this.typesCount)
      .filter(code => !this.excludedCodes.includes(code))
      .map(code => this.typesCount?.[code as NotificationCode]?.count || 0)
      .reduce((total, count) => total + count, 0)
  }

  @Mutation
  setCurrentCodes(codes: NotificationCode[]) {
    this.currentCodes = codes
  }

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

      this.excludedCodes.push(type)
    }

    LocalStorageHelper.saveExcludedTypes(this.excludedCodes)
  }

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

      if (this.excludedCodes.includes(type)) {
        this.excludedCodes.splice(this.excludedCodes.indexOf(type), 1)
      }
    } else {
      this.excludedPopupCodes.push(type)
    }

    LocalStorageHelper.saveExcludedTypes(this.excludedCodes)
    LocalStorageHelper.saveExcludedPopupTypes(this.excludedPopupCodes)
  }

  @Mutation
  setInitializeWebsocket() {
    this.isInitialisedWebsocket = true
  }

  @Mutation
  setExcludedCodes(types: string[]) {
    this.excludedCodes = types
  }

  @Mutation
  setExcludedPopupCodes(types: string[]) {
    this.excludedPopupCodes = types
  }

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

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

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

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

  @Mutation
  addNotification(notification: NotificationEvent) {
    this.notifications.unshift(notification)

    const counting = this.typesCount[notification.typeCode]

    if (counting) {
      counting.count = (counting.count || 0) + 1
    }

    if (!this.excludedPopupCodes.includes(notification.typeCode)) {
      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 counting = this.typesCount[closedNotification.typeCode]

      if (counting) {
        counting.count = (counting.count || 1) - 1
      }
    }
  }

  @Mutation
  clearNotifications() {
    this.notifications = []
    this.currentCodes = []
    this.typesCount = {}
  }

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

  @Action
  loadNotifications() {
    this.setIsLoading(true)

    return NotificationService.getNotifications({
      codes: this.currentCodes,
      excludedCodes: this.excludedCodes,
    }).then(payload => {
      this.setNotifications(payload.notifications)
      this.setIsLoading(false)
    })
  }

  @Action
  loadNotificationsOnScroll() {
    return NotificationService.getNotifications({
      count: this.notifications.length,
      codes: this.currentCodes,
      excludedCodes: this.excludedCodes,
    }).then(payload => {
      this.setNotifications([...this.notifications, ...payload.notifications])

      return payload.notifications
    })
  }

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

    return NotificationService.closeNotification(notificationId)
  }

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

  @Action
  async initWebsocket(userId: string) {
    if (this.isInitialisedWebsocket) {
      return
    }

    this.setInitializeWebsocket()

    try {
      const token = await AuthService.getAuthToken()

      NotificationWebsocketService.init({ authToken: token, clientUid: userId })

      NotificationWebsocketService.onNewNotification(({ notification }) => {
        this.addNotification(notification)
      })
        .onCloseNotification(({ notification }) => {
          this.deleteNotification(notification.id)
          this.removePopupNotification(notification.id)
        })
        .onCloseAllNotifications(() => {
          this.clearNotifications()
        })
    } catch (e) {
      NotifyHelper.error('Ошибка подключения к сокетам')
    }
  }
}
