import { client, xml } from "@xmpp/client"
import uuid from "uuid"

const GROUP_MESSAGE_ID = "I smell JOJO!" // Ur. Ugly.
const XMPP_STATUS_ROOM_CREATED = "201" // 201 Created

export default class XMPPConnection extends EventTarget {
  joinChannel() {
    const channelIdent = `${this.channel}@conference.xmpp.lets-draw.live/${this.username}`
    const presence = xml(
      "presence",
      { to: channelIdent },
      xml("x", { xmlns: "http://jabber.org/protocol/muc" }),
    )
    this.sendOrQueue(presence)
  }

  sendOrQueue(message) {
    if (this.online) {
      this.xmpp.send(message)
    } else {
      this.queue.push(message)
    }
  }

  sendChannelMessage(message) {
    const channelIdent = `${this.channel}@conference.xmpp.lets-draw.live`
    const wrappedMessage = xml(
      "message",
      {
        type: "groupchat",
        to: channelIdent,
        id: GROUP_MESSAGE_ID,
      },
      xml("body", {}, message),
    )

    this.sendOrQueue(wrappedMessage)
  }

  acceptDefaultRoomConfiguration() {
    const channelIdent = `${this.channel}@conference.xmpp.lets-draw.live`
    const presence = xml(
      "iq",
      { id: GROUP_MESSAGE_ID, to: channelIdent, type: "set" },
      xml(
        "query",
        { xmlns: "http://jabber.org/protocol/muc#owner" },
        xml("x", { xmlns: "jabber:x:data", type: "submit" }),
      ),
    )
    this.sendOrQueue(presence)
  }

  constructor(channel) {
    super()

    this.username = uuid.v4().toString()
    this.channel = channel
    this.online = false
    this.queue = []

    const xmpp = client({
      service: "wss://xmpp.lets-draw.live:5281/xmpp-websocket",
      domain: "xmpp.lets-draw.live",
      username: "beartest",
      password: "beartest",
    })

    this.xmpp = xmpp

    xmpp.on("offline", () => {
      this.online = false
    })

    xmpp.on("stanza", (stanza) => {
      const stanzaId = stanza.getAttr("id")
      if (stanzaId && stanzaId === GROUP_MESSAGE_ID) {
        // Messages sent to the room as echoed back
        // Ignore our own messages to prevent loops
        return
      }

      if (stanza.is("message")) {
        this.dispatchEvent(
          new CustomEvent("stanza", {
            detail: stanza,
          }),
        )
      } else if (stanza.is("presence")) {
        const x = stanza.getChild("x")
        if (x === undefined) {
          // Uncertain if this element is guaranteed inside a <presence/>
          return
        }

        const created =
          x.getChildByAttr("code", XMPP_STATUS_ROOM_CREATED) !== undefined
        if (created) {
          // Create an "instant room"
          this.acceptDefaultRoomConfiguration()
        }
      }
    })

    xmpp.on("online", async (address) => {
      /*eslint no-unused-vars: ["error", { "args": "none" }]*/

      // Makes itself available
      await xmpp.send(xml("presence"))

      this.online = true

      for (const message of this.queue) {
        await this.xmpp.send(message)
      }
    })

    xmpp.start().catch(console.error)
    this.joinChannel()
  }

  sneakilySendTheOtherTeamOur(secrets) {
    this.sendChannelMessage(secrets)
  }
}