import {
  Action,
  config,
  Module,
  Mutation,
  VuexModule,
} from 'vuex-module-decorators'
import { NotifyHelper } from '@/helpers'
import { MessageThread } from '@/widgets/messages/types/MessageThread'
import { FoundMessage, Message } from '@/widgets/messages/types/Message'
import { MessageFilterType } from '@/widgets/messages/types/CountingMessages'
import { PreviewMessages } from '@/widgets/messages/types/PreviewMessages'
import { ThreadSubject } from '@/widgets/messages/types/ThreadSubject'
import {
  CreateMessageDto,
  ReplyMessageDto,
  SearchMessagesDto,
  UpdateMessageDto,
} from '@/widgets/messages/dto/MessageDto'
import MessageWebsocketService from '@/widgets/messages/services/MessageWebsocketService'
import MessageHttpService from '@/widgets/messages/services/MessageHttpService'
import ThreadService, {
  ChangeThreadOrderPayload,
  ChangeThreadSubjectPayload,
  GetThreadsPayload,
  UpdateManagersInThread,
} from '@/widgets/messages/services/ThreadService'
import AuthService from '@/widgets/messages/services/AuthService'

config.rawError = true

export const MAX_DEFAULT_MESSAGES_SIZE = 10

@Module({
  name: 'MessageModule',
  namespaced: true,
})
export default class MessageModule extends VuexModule {
  threads: MessageThread[] = []
  subjects: ThreadSubject[] = []
  messageFilterType: MessageFilterType | null = null
  orderName = ''
  currentThreadId: null | number = null
  foundMessages: FoundMessage[] = []
  isInitialisedWebsocket = false

  get isCoffeeTime() {
    return (
      this.messageFilterType === null &&
      this.orderName === null &&
      this.threads.length === 0
    )
  }

  get getPreviewMessages() {
    return (thread: MessageThread): PreviewMessages | null => {
      if (thread.messages.length < MAX_DEFAULT_MESSAGES_SIZE) {
        return null
      }

      // Показываем 2 первых
      // Сворачиваем остальные кроме последних 2
      return {
        first: thread.messages.slice(0, 2),
        last: thread.messages.slice(-2),
      } as PreviewMessages
    }
  }

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

  @Mutation
  setThreads(threads: MessageThread[]) {
    this.threads = threads
  }

  @Mutation
  setFoundMessages(messages: FoundMessage[]) {
    this.foundMessages = messages
  }

  @Mutation
  addFoundMessages(messages: FoundMessage[]) {
    this.foundMessages = [...this.foundMessages, ...messages]
  }

  @Mutation
  setCurrentThreadId(threadId: number) {
    this.currentThreadId = threadId
  }

  @Mutation
  setOrderName(orderName: string) {
    this.orderName = orderName
  }

  @Mutation
  removeThread(threadId: number) {
    const threadIndex = this.threads.findIndex(thread => thread.id === threadId)

    if (threadIndex !== -1) {
      this.threads.splice(threadIndex, 1)
    }
  }

  @Mutation
  removeMessageFromThread(payload: { messageId: number; threadId: number }) {
    const thread = this.threads.find(thread => thread.id === payload.threadId)

    if (thread) {
      if (thread.messages.length === 1) {
        const threadIndex = this.threads.findIndex(
          thread => thread.id === payload.threadId
        )

        if (threadIndex !== -1) {
          this.threads.splice(threadIndex, 1)
        }
      } else {
        const index = thread.messages.findIndex(m => m.id === payload.messageId)

        if (index !== -1) {
          thread.messages.splice(index, 1)
        }
      }
    }
  }

  @Mutation
  setSubjects(subjects: ThreadSubject[]) {
    this.subjects = subjects
  }

  @Mutation
  setFilterType(filterType: MessageFilterType | null) {
    this.messageFilterType = filterType
  }

  @Mutation
  addMessage(message: Message) {
    const thread = this.threads.find(thread => thread.id === message.threadId)

    if (thread) {
      thread.messages.push(message)
    }
  }

  @Mutation
  updateMessage(message: Message) {
    const thread = this.threads.find(thread => thread.id === message.threadId)

    if (thread) {
      const messageIndex = thread.messages.findIndex(m => m.id === message.id)

      if (messageIndex !== -1) {
        thread.messages.splice(messageIndex, 1, message)
      }
    }
  }

  @Mutation
  addThread(thread: MessageThread) {
    this.threads.unshift(thread)
  }

  @Mutation
  updateThread(thread: MessageThread) {
    const threadIndex = this.threads.findIndex(t => t.id === thread.id)

    if (threadIndex !== -1) {
      this.threads.splice(threadIndex, 1, thread)
    }
  }

  @Action
  loadSubjects() {
    return ThreadService.getThreadSubjects().then(subjects =>
      this.setSubjects(subjects)
    )
  }

  @Action
  loadThreads(payload?: GetThreadsPayload) {
    return ThreadService.getThreads(payload).then(threads =>
      this.setThreads(threads)
    )
  }

  @Action
  infinityLoadThreads(payload: GetThreadsPayload) {
    const ids = this.threads.map(thread => thread.id)

    return ThreadService.getThreads({ ...payload, ids }).then(threads => {
      this.setThreads([...this.threads, ...threads])

      return threads
    })
  }

  @Action
  closeThread(threadId: number) {
    return ThreadService.closeThread(threadId)
      .then(thread => {
        this.updateThread(thread)
        NotifyHelper.success('Обработано')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  openThread(threadId: number) {
    return ThreadService.openThread(threadId)
      .then(thread => {
        this.updateThread(thread)
        NotifyHelper.success('В работе!')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  addThreadToFavorite(threadId: number) {
    const thread = this.threads.find(t => t.id === threadId) as MessageThread
    this.updateThread({ ...thread, isFavorite: true })

    return ThreadService.addThreadToFavorite(threadId)
      .then(() => {
        NotifyHelper.success('Добавлено в избранное')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  removeThreadFromFavorite(threadId: number) {
    const thread = this.threads.find(t => t.id === threadId) as MessageThread
    this.updateThread({ ...thread, isFavorite: false })

    return ThreadService.removeThreadFromFavorite(threadId)
      .then(() => {
        NotifyHelper.success('Удалено из избранного')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  changeThreadSubject(payload: ChangeThreadSubjectPayload) {
    return ThreadService.changeThreadSubject(payload)
      .then(thread => {
        this.updateThread(thread)

        NotifyHelper.success('Тема изменена')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  changeThreadOrder(payload: ChangeThreadOrderPayload) {
    return ThreadService.changeThreadOrder(payload)
      .then(thread => {
        this.updateThread(thread)

        NotifyHelper.success('Привязано к заказу')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  updateManagersInThread(payload: UpdateManagersInThread) {
    return ThreadService.updateManagersInThread(payload)
      .then(() => NotifyHelper.success('Подписчики обновлены'))
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  createThread(dto: CreateMessageDto) {
    return MessageHttpService.createThread(dto)
  }

  @Action
  sendReplyMessage(dto: ReplyMessageDto) {
    return MessageHttpService.replyMessage(dto).then(thread => {
      this.updateThread(thread)
      NotifyHelper.success('Отправлено')
    })
  }

  @Action
  saveMessage(dto: UpdateMessageDto) {
    return MessageHttpService.updateMessage(dto)
      .then(savedMessage => {
        this.updateMessage(savedMessage)
        NotifyHelper.success('Сохранено')
      })
      .catch(() => NotifyHelper.serverError())
  }

  @Action
  removeMessage(payload: { messageId: number; threadId: number }) {
    return MessageHttpService.removeMessage(payload.messageId).then(() => {
      this.removeMessageFromThread(payload)
      NotifyHelper.success('Удалено')
    })
  }

  @Action
  loadThreadById(threadId: number) {
    this.setCurrentThreadId(threadId)

    return ThreadService.getThreadById(threadId).then(thread =>
      this.setThreads([thread])
    )
  }

  @Action
  searchMessages(dto: SearchMessagesDto & { withPaginate?: boolean }) {
    return MessageHttpService.searchMessages(dto).then(messages => {
      dto.withPaginate
        ? this.addFoundMessages(messages)
        : this.setFoundMessages(messages)

      return messages
    })
  }

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

    this.setInitializeWebsocket()

    try {
      const authToken = await AuthService.getAuthToken()

      MessageWebsocketService.init(authToken)

      MessageWebsocketService.onNewThread(payload =>
        this.addThread(payload.thread)
      )

      MessageWebsocketService.onUpdateThread(payload => {
        this.updateThread(payload.thread)
      })

      MessageWebsocketService.onDeleteThread(payload =>
        this.removeThread(payload.thread.id)
      )

      MessageWebsocketService.onUpdateMessage(payload =>
        this.updateMessage(payload.message)
      )

      MessageWebsocketService.onDeleteMessage(payload =>
        this.removeMessageFromThread({
          threadId: payload.message.threadId,
          messageId: payload.message.id,
        })
      )
    } catch (e) {
      NotifyHelper.error('Ошибка подключения к сокетам')
    }
  }
}
