import { Controller } from '@hotwired/stimulus'
import NoSleep from 'nosleep.js'

export default class extends Controller {
  static targets = [
    "videoMute",
    "audioMute",
    "timer",
    "permissionError",
    "otherLeftError",
    "offlineError",
    "waitingLabel"
  ]

  disconnect() {
    console.log("CALL DISCONNECT")
    clearInterval(this.timer)
    if (this.janus) {
      console.log("JANUS DESTROY")
      this.janus.destroy({
        cleanupHandles: true
      })
    }
    if (this.noSleep)
      this.noSleep.disable()
  }

  finish(ev, action = "finish") {
    if (ev)
      ev.preventDefault()

    const el = document.querySelector("[id*='chat_service_request']")
    const serviceRequestId = el.id.replace('chat_service_request_', '')
    const url = `/${this.data.get('role')}/service_requests/${serviceRequestId}/${action}`

    if (serviceRequestId) {
      fetch(url, {
        headers: {
          "Content-Type": "application/json",
          "X-CSRF-Token": document.querySelector("meta[name='csrf-token']").
            getAttribute("content")
        },
        method: action === "finish" ? "PUT" : "DELETE",
        redirect: 'follow'
      })
      .then(response => {
        if (response.redirected) {
          this.callInProgress = false
          location.href = response.url
        }
      })
    }
  }

  cancel() {
    this.finish(undefined, "cancel")
  }

  connect() {
    if (this.data.get("is-agent") == "true" || this.data.get("status") === "in_progress")
      this.startCall()

    // Enable wakelock
    // Usually a user interaction is required, but since we use Turbo it works anyway
    this.noSleep = new NoSleep()
    this.noSleep.enable()
  }

  startTimer() {
    this.callStartedAt = Date.now()
    this.timerTarget.classList.remove("hidden")
    this.timer = setInterval(() => {
      const elapsedSeconds = (Date.now() - this.callStartedAt) / 1000
      const date = new Date(0)
      date.setSeconds(elapsedSeconds)
      const timeString = date.toISOString().substr(14, 5)
      this.timerTarget.innerHTML = timeString
    }, 1000)
  }

  showPermissionError(show = true) {
    this.permissionErrorTarget.classList.remove("hidden", !show)
  }

  showOtherLeftError(show = true) {
    this.otherLeftErrorTarget.classList.toggle("hidden", !show)
    document.querySelector("#remote-video").classList.toggle("hidden", show)
  }

  showOfflineError(show = true) {
    this.offlineErrorTarget.classList.toggle("hidden", !show)
    document.querySelector("#remote-video").classList.toggle("hidden", show)
  }

  // For clients, start call via this event
  startCall() {
    console.log("STARTCALL")
    
    // Check camera / microphone permissions
    // If permission check succeeds, init Janus, else show error message
    let controller = this
    navigator.mediaDevices.getUserMedia({ audio: true, video: true })
      .then(function(stream) {
        if (
          stream.getVideoTracks().length > 0 &&
          stream.getAudioTracks().length > 0
        ) {
          controller.handleBrowserState()
          controller.initJanus()
        } else {
          controller.showPermissionError()
        }
      }).catch(function (error) {
        controller.showPermissionError()
      })
  }

  handleBrowserState() {
    let controller = this

    window.addEventListener("offline", function() {
      controller.showOfflineError()
    })

    window.addEventListener("online", function() {
      location.reload()
    })
  }

  initJanus() {
    console.log("INIT JANUS")
    
    let controller = this
    Janus.init({
      debug: true, callback: function() {

        // Detect browser WebRTC support
        if (!Janus.isWebrtcSupported())
          alert("No WebRTC support")

        console.log("BEFORE NEW JANUS")

        // Connect to Janus server
        controller.janus = new Janus({
          server: [
            "wss://" + controller.data.get("janus-url") + "/janus-websocket"
          ],
          token: controller.data.get("janus-auth-token"),
          success: function() {
            controller._attachPublisherHandle()
          },
          error: function() {
            console.log("Could not init janus for speaker")
          }
        })

        console.log("AFTER NEW JANUS")

      }
    })
  }

  toggleAudioMute() {

    // Set muted state on server
    fetch(this.data.get("audio-mute-url"), {
      headers: {
        "Content-Type": "application/json",
        "X-CSRF-Token": document.querySelector("meta[name='csrf-token']").
          getAttribute("content")
      },
      method: "PATCH"
    })

    if (this.publisherHandle === undefined) return

    if (this.audioMuteTarget.checked) {
      this.publisherHandle.unmuteAudio()
    } else {
      this.publisherHandle.muteAudio()
    }
  }

  toggleVideoMute() {

    // Set muted state on server
    fetch(this.data.get("video-mute-url"), {
      headers: {
        "Content-Type": "application/json",
        "X-CSRF-Token": document.querySelector("meta[name='csrf-token']").
          getAttribute("content")
      },
      method: "PATCH"
    })

    if (this.publisherHandle === undefined) return

    if (this.videoMuteTarget.checked) {
      this.publisherHandle.unmuteVideo()
    } else {
      this.publisherHandle.muteVideo()
    }
  }

  _attachPublisherHandle() {
    let controller = this

    controller.janus.attach({
      plugin: "janus.plugin.videoroom",

      success: function(pluginHandle) {
        console.log("Plugin attached as publisher!")
        controller.publisherHandle = pluginHandle

        // Join as publisher
        controller.publisherHandle.send({
          message: {
            request: "join",
            room: controller.data.get("janus-room"),
            ptype: "publisher",
            pin: controller.data.get("janus-pin")
          }
        })
      },

      mediaState: function(_, receiving) {
        if (receiving) {
          console.log("Receiving publisher")
        }
      },

      onmessage: function(msg, jsep) {
        console.log("--- Publisher Message")
        console.log(msg, jsep)

        if (msg.videoroom == "joined" && !controller.publisherHandleId) {
          controller.callInProgress = true
          controller.publisherHandleId = msg.id

          // Start publishing
          controller.publisherHandle.createOffer({
            media: {
              audioRecv: false,
              videoRecv: false,
              audioSend: true,
              videoSend: true
            },
            success: function(jsep) {
              console.log(jsep)
              controller.publisherHandle.send({
                message: { request: "publish" }, jsep: jsep
              })
            },
            error: function (error) {
              console.log("WebRTC error:", error)
            }
          })

        // If any publisher left the call
        } else if (msg.unpublished) {
          controller.showOtherLeftError()
        }

        // If any other publishers are provided, attach subscriber handle
        // Publishers are also sent together with the "joined" event
        // In some occasions the own stream is broadcasted, hence
        // we have to prevent subscribing to self
        if (msg.publishers && msg.publishers.length) {
          let publisher = msg.publishers.find(element =>
            controller.publisherHandleId != element.id
          )
          controller._attachSubscriberHandle(publisher)
        }

        // Handle jsep
        if (jsep != undefined && jsep != null) {
          controller.publisherHandle.handleRemoteJsep({ jsep: jsep })
        }
      },

      // Display my own stream
      onlocalstream: function(stream) {
        console.log("Got a local stream", stream)

        // Always mute own video stream
        document.querySelector("#local-video").muted = "muted"

        // Handle muted states when starting the stream
        if (controller.videoMuteTarget.checked)
          controller.publisherHandle.unmuteVideo()
        else
          controller.publisherHandle.muteVideo()

        if (controller.audioMuteTarget.checked)
          controller.publisherHandle.unmuteAudio()
        else
          controller.publisherHandle.muteAudio()

        // Attach local stream to video element
        Janus.attachMediaStream(
          document.querySelector("#local-video"), stream
        )
      }
    })
  }

  _attachSubscriberHandle(publisher) {
    let controller = this

    controller.janus.attach({
      plugin: "janus.plugin.videoroom",

      success: function(pluginHandle) {
        console.log("Plugin attached as subscriber!")
        controller.subscriberHandle = pluginHandle

        // Join as subscriber
        controller.subscriberHandle.send({
          message: {
            request: "join",
            room: controller.data.get("janus-room"),
            ptype: "subscriber",
            pin: controller.data.get("janus-pin"),
            feed: publisher.id
          }
        })
      },

      onmessage: function(msg, jsep) {
        console.log("--- Subscriber Message")
        console.log(msg, jsep)

        if (jsep) {

          // Start watching/listening to subscription
          controller.subscriberHandle.createAnswer({
            jsep: jsep,
            media: { audio: false, video: false },
            success: function(jsep) {
              console.log(jsep)
              controller.subscriberHandle.send({
                message: {
                  request: "start",
                  room: controller.data.get("janus-room")
                }, jsep: jsep
              })
            },
            error: function (error) {
              console.log("WebRTC error:", error)
            }
          })
        }
      },

      // Display other stream(s)
      onremotestream: function(stream) {
        console.log("Got a remote stream", stream)
        if (stream.active) {
          controller.startTimer()
          controller.showOtherLeftError(false)
          if (controller.hasWaitingLabelTarget)
            controller.waitingLabelTarget.classList.add("hidden")
          Janus.attachMediaStream(
            document.querySelector("#remote-video"), stream
          )
        }
      }
    })
  }
}
