<template>
  <Splitpanes
    class="smart-splitter"
    :rtl="SIDEBAR_ON_LEFT && windowInnerWidth.x.value >= 768"
    :class="{
      '!flex-row-reverse': SIDEBAR_ON_LEFT && windowInnerWidth.x.value >= 768,
    }"
    :horizontal="!(windowInnerWidth.x.value >= 768)"
  >
    <Pane size="75" min-size="65" class="hide-scrollbar !overflow-auto">
      <Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
        <Pane
          :size="COLUMN_LAYOUT ? 45 : 50"
          class="hide-scrollbar !overflow-auto"
        >
          <AppSection label="request">
            <div
              class="bg-primary flex flex-col space-y-4 p-4 top-0 z-10 sticky"
            >
              <div class="space-x-2 flex-1 inline-flex">
                <input
                  id="mqtt-url"
                  v-model="url"
                  type="url"
                  autocomplete="off"
                  spellcheck="false"
                  class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
                  :placeholder="$t('mqtt.url')"
                  :disabled="connectionState"
                  @keyup.enter="validUrl ? toggleConnection() : null"
                />
                <ButtonPrimary
                  id="connect"
                  :disabled="!validUrl"
                  class="w-32"
                  :label="
                    connectionState
                      ? $t('action.disconnect')
                      : $t('action.connect')
                  "
                  :loading="connectingState"
                  @click.native="toggleConnection"
                />
              </div>
              <div class="flex space-x-4">
                <input
                  id="mqtt-username"
                  v-model="username"
                  type="text"
                  spellcheck="false"
                  class="input"
                  :placeholder="$t('authorization.username')"
                />
                <input
                  id="mqtt-password"
                  v-model="password"
                  type="password"
                  spellcheck="false"
                  class="input"
                  :placeholder="$t('authorization.password')"
                />
              </div>
            </div>
          </AppSection>
        </Pane>
        <Pane
          :size="COLUMN_LAYOUT ? 65 : 50"
          class="hide-scrollbar !overflow-auto"
        >
          <AppSection label="response">
            <RealtimeLog :title="$t('mqtt.log')" :log="log" />
          </AppSection>
        </Pane>
      </Splitpanes>
    </Pane>
    <Pane
      v-if="SIDEBAR"
      size="25"
      min-size="20"
      class="hide-scrollbar !overflow-auto"
    >
      <AppSection label="messages">
        <div class="flex flex-col flex-1 p-4 inline-flex">
          <label for="pub_topic" class="font-semibold text-secondaryLight">
            {{ $t("mqtt.topic") }}
          </label>
        </div>
        <div class="flex px-4">
          <input
            id="pub_topic"
            v-model="pub_topic"
            class="input"
            :placeholder="$t('mqtt.topic_name')"
            type="text"
            autocomplete="off"
            spellcheck="false"
          />
        </div>
        <div class="flex flex-1 p-4 items-center justify-between">
          <label for="mqtt-message" class="font-semibold text-secondaryLight">
            {{ $t("mqtt.communication") }}
          </label>
        </div>
        <div class="flex space-x-2 px-4">
          <input
            id="mqtt-message"
            v-model="msg"
            class="input"
            type="text"
            autocomplete="off"
            :placeholder="$t('mqtt.message')"
            spellcheck="false"
          />
          <ButtonPrimary
            id="publish"
            name="get"
            :disabled="!canpublish"
            :label="$t('mqtt.publish')"
            @click.native="publish"
          />
        </div>
        <div
          class="border-t border-dividerLight flex flex-col flex-1 mt-4 p-4 inline-flex"
        >
          <label for="sub_topic" class="font-semibold text-secondaryLight">
            {{ $t("mqtt.topic") }}
          </label>
        </div>
        <div class="flex space-x-2 px-4">
          <input
            id="sub_topic"
            v-model="sub_topic"
            type="text"
            autocomplete="off"
            :placeholder="$t('mqtt.topic_name')"
            spellcheck="false"
            class="input"
          />
          <ButtonPrimary
            id="subscribe"
            name="get"
            :disabled="!cansubscribe"
            :label="
              subscriptionState ? $t('mqtt.unsubscribe') : $t('mqtt.subscribe')
            "
            reverse
            @click.native="toggleSubscription"
          />
        </div>
      </AppSection>
    </Pane>
  </Splitpanes>
</template>

<script>
import { defineComponent } from "@nuxtjs/composition-api"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
import Paho from "paho-mqtt"
import debounce from "lodash/debounce"
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
import { useSetting } from "~/newstore/settings"
import useWindowSize from "~/helpers/utils/useWindowSize"
import {
  MQTTEndpoint$,
  setMQTTEndpoint,
  MQTTConnectingState$,
  MQTTConnectionState$,
  setMQTTConnectingState,
  setMQTTConnectionState,
  MQTTSubscriptionState$,
  setMQTTSubscriptionState,
  MQTTSocket$,
  setMQTTSocket,
  MQTTLog$,
  setMQTTLog,
  addMQTTLogLine,
} from "~/newstore/MQTTSession"
import { useStream } from "~/helpers/utils/composables"

export default defineComponent({
  components: { Splitpanes, Pane },
  setup() {
    return {
      windowInnerWidth: useWindowSize(),
      SIDEBAR: useSetting("SIDEBAR"),
      COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
      SIDEBAR_ON_LEFT: useSetting("SIDEBAR_ON_LEFT"),
      url: useStream(MQTTEndpoint$, "", setMQTTEndpoint),
      connectionState: useStream(
        MQTTConnectionState$,
        false,
        setMQTTConnectionState
      ),
      connectingState: useStream(
        MQTTConnectingState$,
        false,
        setMQTTConnectingState
      ),
      subscriptionState: useStream(
        MQTTSubscriptionState$,
        false,
        setMQTTSubscriptionState
      ),
      log: useStream(MQTTLog$, null, setMQTTLog),
      client: useStream(MQTTSocket$, null, setMQTTSocket),
    }
  },
  data() {
    return {
      isUrlValid: true,
      pub_topic: "",
      sub_topic: "",
      msg: "",
      manualDisconnect: false,
      username: "",
      password: "",
    }
  },
  computed: {
    validUrl() {
      return this.isUrlValid
    },
    canpublish() {
      return this.pub_topic !== "" && this.msg !== "" && this.connectionState
    },
    cansubscribe() {
      return this.sub_topic !== "" && this.connectionState
    },
  },
  watch: {
    url() {
      this.debouncer()
    },
  },
  created() {
    if (process.browser) {
      this.worker = this.$worker.createRejexWorker()
      this.worker.addEventListener("message", this.workerResponseHandler)
    }
  },
  destroyed() {
    this.worker.terminate()
  },
  methods: {
    debouncer: debounce(function () {
      this.worker.postMessage({ type: "ws", url: this.url })
    }, 1000),
    workerResponseHandler({ data }) {
      if (data.url === this.url) this.isUrlValid = data.result
    },
    connect() {
      this.connectingState = true
      this.log = [
        {
          payload: this.$t("state.connecting_to", { name: this.url }),
          source: "info",
          color: "var(--accent-color)",
          ts: new Date().toLocaleTimeString(),
        },
      ]
      const parseUrl = new URL(this.url)
      this.client = new Paho.Client(
        `${parseUrl.hostname}${
          parseUrl.pathname !== "/" ? parseUrl.pathname : ""
        }`,
        parseUrl.port !== "" ? Number(parseUrl.port) : 8081,
        "hoppscotch"
      )
      const connectOptions = {
        onSuccess: this.onConnectionSuccess,
        onFailure: this.onConnectionFailure,
        useSSL: parseUrl.protocol !== "ws:",
      }
      if (this.username !== "") {
        connectOptions.userName = this.username
      }
      if (this.password !== "") {
        connectOptions.password = this.password
      }
      this.client.connect(connectOptions)
      this.client.onConnectionLost = this.onConnectionLost
      this.client.onMessageArrived = this.onMessageArrived

      logHoppRequestRunToAnalytics({
        platform: "mqtt",
      })
    },
    onConnectionFailure() {
      this.connectingState = false
      this.connectionState = false
      addMQTTLogLine({
        payload: this.$t("error.something_went_wrong"),
        source: "info",
        color: "#ff5555",
        ts: new Date().toLocaleTimeString(),
      })
    },
    onConnectionSuccess() {
      this.connectingState = false
      this.connectionState = true
      addMQTTLogLine({
        payload: this.$t("state.connected_to", { name: this.url }),
        source: "info",
        color: "var(--accent-color)",
        ts: new Date().toLocaleTimeString(),
      })
      this.$toast.success(this.$t("state.connected"))
    },
    onMessageArrived({ payloadString, destinationName }) {
      addMQTTLogLine({
        payload: `Message: ${payloadString} arrived on topic: ${destinationName}`,
        source: "info",
        color: "var(--accent-color)",
        ts: new Date().toLocaleTimeString(),
      })
    },
    toggleConnection() {
      if (this.connectionState) {
        this.disconnect()
      } else {
        this.connect()
      }
    },
    disconnect() {
      this.manualDisconnect = true
      this.client.disconnect()
      addMQTTLogLine({
        payload: this.$t("state.disconnected_from", { name: this.url }),
        source: "info",
        color: "#ff5555",
        ts: new Date().toLocaleTimeString(),
      })
    },
    onConnectionLost() {
      this.connectingState = false
      this.connectionState = false
      if (this.manualDisconnect) {
        this.$toast.error(this.$t("state.disconnected"))
      } else {
        this.$toast.error(this.$t("error.something_went_wrong"))
      }
      this.manualDisconnect = false
      this.subscriptionState = false
    },
    publish() {
      try {
        this.client.publish(this.pub_topic, this.msg, 0, false)
        addMQTTLogLine({
          payload: `Published message: ${this.msg} to topic: ${this.pub_topic}`,
          ts: new Date().toLocaleTimeString(),
          source: "info",
          color: "var(--accent-color)",
        })
      } catch (e) {
        addMQTTLogLine({
          payload:
            this.$t("error.something_went_wrong") +
            `while publishing msg: ${this.msg} to topic:  ${this.pub_topic}`,
          source: "info",
          color: "#ff5555",
          ts: new Date().toLocaleTimeString(),
        })
      }
    },
    toggleSubscription() {
      if (this.subscriptionState) {
        this.unsubscribe()
      } else {
        this.subscribe()
      }
    },
    subscribe() {
      try {
        this.client.subscribe(this.sub_topic, {
          onSuccess: this.usubSuccess,
          onFailure: this.usubFailure,
        })
      } catch (e) {
        addMQTTLogLine({
          payload:
            this.$t("error.something_went_wrong") +
            `while subscribing to topic:  ${this.sub_topic}`,
          source: "info",
          color: "#ff5555",
          ts: new Date().toLocaleTimeString(),
        })
      }
    },
    usubSuccess() {
      this.subscriptionState = !this.subscriptionState
      addMQTTLogLine({
        payload:
          `Successfully ` +
          (this.subscriptionState ? "subscribed" : "unsubscribed") +
          ` to topic: ${this.sub_topic}`,
        source: "info",
        color: "var(--accent-color)",
        ts: new Date().toLocaleTimeString(),
      })
    },
    usubFailure() {
      addMQTTLogLine({
        payload:
          `Failed to ` +
          (this.subscriptionState ? "unsubscribe" : "subscribe") +
          ` to topic: ${this.sub_topic}`,
        source: "info",
        color: "#ff5555",
        ts: new Date().toLocaleTimeString(),
      })
    },
    unsubscribe() {
      this.client.unsubscribe(this.sub_topic, {
        onSuccess: this.usubSuccess,
        onFailure: this.usubFailure,
      })
    },
  },
})
</script>