import io from 'socket.io-client'
import Echo from 'laravel-echo'
import { MessageThread } from '@/widgets/messages/types/MessageThread'
import { Message } from '@/widgets/messages/types/Message'
import { UpdateCountingDto } from '@/widgets/messages/dto/UpdateCountingDto'
import { Channel } from 'laravel-echo/dist/channel'

export type onWhisper = (payload: { threadId: number; userId: string }) => void
export type onThread = (payload: { thread: MessageThread }) => void
export type onMessage = (message: { message: Message }) => void
export type onUpdateCounting = (payload: UpdateCountingDto) => void

export enum Events {
  newMessage = 'MessageCreated',
  deleteMessage = 'MessageDeleted',
  updateMessage = 'MessageUpdated',
  newThread = 'ThreadCreated',
  updateThread = 'ThreadUpdated',
  deleteThread = 'ThreadDeleted',
  updateCounting = 'CountingUpdated',
  whisper = 'typing',
}

window.io = io

class MessageWebsocketService {
  client!: Echo

  init(token: string) {
    if (this.client) {
      return
    }

    this.client = new Echo({
      broadcaster: 'socket.io',
      key: 'random',
      cluster: 'mt1',
      host: process.env.MESSAGES_WS_URL,
      transports: ['websocket', 'polling', 'flashsocket'],
      auth: {
        headers: {
          Authorization: 'Bearer ' + token,
        },
      },
    })
  }

  public onUpdateMessage(cb: onMessage) {
    this.getBroadcastChannel().listen(Events.updateMessage, cb)
  }

  public onDeleteMessage(cb: onMessage) {
    this.getBroadcastChannel().listen(Events.deleteMessage, cb)
  }

  public onNewThread(cb: onThread) {
    this.getBroadcastChannel().listen(Events.newThread, cb)
  }

  public onUpdateThread(cb: onThread) {
    this.getBroadcastChannel().listen(Events.updateThread, cb)
  }

  public onDeleteThread(cb: onThread) {
    this.getBroadcastChannel().listen(Events.deleteThread, cb)
  }

  public onUpdateCounting(cb: onUpdateCounting) {
    this.privateChannel.listen(Events.updateCounting, cb)
  }

  public whisper(threadId: number, userId: string) {
    const channel = this.client.private(`thread.${threadId}`) as Channel & {
      whisper: Function
    }

    channel.whisper(Events.whisper, {
      typing: true,
      userId,
    })
  }

  public onWhisper(threadId: number, cb: onWhisper) {
    this.client
      .private(`thread.${threadId}`)
      .listenForWhisper(Events.whisper, cb)
  }

  public offWhisper(threadId: number) {
    this.client
      .private(`thread.${threadId}`)
      .stopListeningForWhisper(Events.whisper)
  }

  private getBroadcastChannel() {
    return this.client.channel('broadcast')
  }

  private get privateChannel() {
    return this.client.channel('broadcast')
  }
}

export default new MessageWebsocketService()
