import { Controller } from "@hotwired/stimulus"
import Sortable from "sortablejs"

/***
 * Treeview controller
 *
 * Manages the tree view
 */
export default class extends Controller {
  static targets = ["folderTemplate", "entryTemplate", "collapseExpand", "contentItems"]

  connect() {
    const self = this

    self.element.addEventListener("click", (event) => {
      event.stopPropagation()
      let el = event.target.closest("li.entry")
      if (!el) {
        return
      }
      if (el && event.target.closest("li.entry").classList.contains("directory")) {
        el.classList.toggle("open")
        el.classList.toggle("closed")

        self._updateCollapseExpandAll()
      }
    })

    self.element.querySelectorAll("ul").forEach((el) => {
      this._addSortable(el)
    })
  }

  // Collapse or expand all folders
  collapseExpandAll(event) {
    const self = this
    if (self.collapseExpandTarget.classList.contains("fa-square-plus")) {
      self.element.querySelectorAll("li.directory").forEach((el) => {
        el.classList.add("open")
        el.classList.remove("closed")
      })
    } else {
      self.element.querySelectorAll("li.directory").forEach((el) => {
        el.classList.remove("open")
        el.classList.add("closed")
      })
    }
    self._updateCollapseExpandAll()
  }

  // Create content
  create(event) {
    const kind = event.target.closest("[data-action]").getAttribute("data-kind")
    const url = event.target.closest("[data-action]").getAttribute("data-url")

    let template = "entryTemplateTarget"
    if (kind == "folder") {
      template = "folderTemplateTarget"
    }

    const newContentNode = document.importNode(this[template].content, true)

    const closestDirectory = event.target.closest("li.directory")
    let newContentContainer = event.target.closest(".section").querySelector("ul")
    if (closestDirectory) {
      newContentContainer = closestDirectory.querySelector("ul")
    }
    newContentContainer.prepend(newContentNode)

    const input = newContentContainer.querySelector("input")
    input.setAttribute("data-kind", kind)
    input.setAttribute("data-url", url)
    input.focus()
    input.setSelectionRange(0, input.value.length)

    input.addEventListener(
      "keyup",
      function (event) {
        this._createContent(event, newContentContainer)
      }.bind(this)
    )
    input.addEventListener(
      "blur",
      function (event) {
        this._cancelCreate(event, newContentContainer)
      }.bind(this)
    )

    event.stopPropagation()
  }

  // Delete content
  delete(event) {
    const self = this

    event.stopPropagation()
    event.cancelBubble = true

    const elm = event.target.closest("[data-action]")

    let result = true
    if (elm.getAttribute("data-confirm")) {
      result = confirm(elm.getAttribute("data-confirm"))
    }
    if (!result) {
      return
    }

    const formData = new FormData()
    formData.append("_method", "DELETE")
    fetch(elm.getAttribute("data-url"), {
      method: "POST",
      headers: {
        "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
      },
      body: formData,
    }).then((response) => {
      if (response.status == 200) {
        const node = elm.closest("li")
        const contentId = node.getAttribute("data-content")
        if (node.parentNode) {
          window.scriboEditors.close(contentId)
          node.parentNode.removeChild(node)
        }
      }
    })
  }

  // Opens content in the editor pane
  open(event) {
    const self = this
    const closestA = event.target.closest("a")

    // Is a rename currently happening? If so abort
    if (closestA.querySelector("input")) {
      return
    }
    this.clicked = event
    setTimeout(this._open.bind(this, event), 500)
    event.stopPropagation()
    event.preventDefault()
    return false
  }

  // Rename content
  rename(event) {
    const self = this
    this.clicked = null
    const closestA = event.target.closest("a")

    // Is a rename currently happening? If so abort
    if (closestA.querySelector("input")) {
      return
    }

    const input = document.createElement("input")
    const nameSpan = closestA.firstChild
    input.type = "text"
    input.value = nameSpan.innerText

    input.addEventListener("keyup", this._renameContent.bind(this))
    input.addEventListener("blur", this._cancelRename.bind(this))

    closestA.querySelector(".tools").style.display = "none"
    nameSpan.innerText = ""
    nameSpan.appendChild(input)

    input.focus()
    input.setSelectionRange(0, input.value.length)

    event.stopPropagation()
    event.preventDefault()
    return false
  }

  disconnect() {}

  // Private

  _updateCollapseExpandAll() {
    const self = this
    if (!self.contentItemsTarget.querySelector("li.entry.directory.open")) {
      // All are closed
      self.collapseExpandTarget.classList.remove("fa-square-minus")
      self.collapseExpandTarget.classList.add("fa-square-plus")
    } else {
      self.collapseExpandTarget.classList.add("fa-square-minus")
      self.collapseExpandTarget.classList.remove("fa-square-plus")
    }
  }

  _open(event) {
    const self = this

    if (this.clicked == null) {
      return
    }

    this.clicked = null
    event.stopPropagation()
    const closestA = event.target.closest("a")

    fetch(closestA.getAttribute("data-tree-view-url"), {
      method: "GET",
      headers: {
        Accept: "application/json, text/javascript",
      },
    }).then((response) => {
      response.json().then(function (data) {
        window.scriboEditors.open(data.content.id, data.content.path, data.content.full_path, data.content.url, data.html)
        self._selectEntry(closestA.closest("li.entry"))
      })
    })
  }

  _renameContent(event) {
    const self = this

    const node = event.target.closest("li")
    const contentId = node.getAttribute("data-content")

    const closestA = event.target.closest("a")

    const input = closestA.querySelector("input")
    const nameSpan = closestA.querySelector("span.name")
    nameSpan.setAttribute("data-path", input.value)

    const newName = input.value
    if (event.key == "Enter") {
      fetch(closestA.getAttribute("data-tree-view-rename-url"), {
        method: "PUT",
        headers: {
          Accept: "application/json, text/javascript",
          "Content-Type": "application/json",
          "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
        },
        body: JSON.stringify({
          to: newName,
        }),
      }).then((response) => {
        response.json().then(function (data) {
          let changeEvent = new CustomEvent("content-editor.changed", {
            bubbles: true,
            cancelable: true,
            detail: {
              contentId: data.content.id,
              path: data.content.path,
              fullPath: data.content.full_path,
            },
          })
          self.element.dispatchEvent(changeEvent)
          self._cancelRename(event)
        })
      })
    } else if (event.key == "Escape") {
      self._cancelRename(event)
    }
  }

  _cancelRename(event) {
    const closestA = event.target.closest("a")
    const nameSpan = closestA.firstChild
    const input = nameSpan.querySelector("input")
    closestA.querySelector(".tools").style.display = ""

    const name = input.value
    nameSpan.removeChild(input)
    nameSpan.innerText = name
  }

  _createContent(event, newContentContainer) {
    const self = this

    const input = newContentContainer.querySelector("input")
    const nameSpan = newContentContainer.querySelector("span.name")
    nameSpan.setAttribute("data-path", input.value)

    if (event.key == "Enter") {
      const parent = newContentContainer.getAttribute("data-parent")

      fetch(input.getAttribute("data-url"), {
        method: "POST",
        headers: {
          Accept: "application/json, text/javascript",
          "Content-Type": "application/json",
          "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
        },
        body: JSON.stringify({
          parent: parent,
          kind: input.getAttribute("data-kind"),
          path: input.value,
        }),
      }).then((response) => {
        if (response.status == 200) {
          response.json().then(function (data) {
            self._cancelCreate(event, newContentContainer)
            newContentContainer.insertAdjacentHTML("afterbegin", data.itemHtml)
            if (data.content.kind != "folder") {
              // document.querySelector(".editor-pane").innerHTML = data.html
              self._selectEntry(newContentContainer.querySelector("li." + (data.content.kind == "folder" ? "folder" : "file")))
              window.scriboEditors.open(data.content.id, data.content.path, data.content.full_path, data.content.url, data.html)
            } else {
              // A folder
              let folderUl = newContentContainer.querySelector(`[data-content="${data.content.id}"] ul`)
              self._addSortable(folderUl)
            }
          })
        }
      })
    } else if (event.key == "Escape") {
      self._cancelCreate(event, newContentContainer)
    }
  }

  _cancelCreate(event, newContentContainer) {
    newContentContainer.removeChild(newContentContainer.firstChild)
  }

  _selectEntry(element) {
    const treeView = document.querySelector(".tree-view")
    const lastSelected = treeView.querySelector("li.entry.selected")
    if (lastSelected) {
      lastSelected.classList.remove("selected")
    }

    element.classList.add("selected")
  }

  _addSortable(el) {
    const self = this
    new Sortable(el, {
      group: "nested",
      animation: 150,
      fallbackOnBody: true,
      swapThreshold: 0.65,
      onEnd: (evt) => {
        const contentId = evt.item.getAttribute("data-content")
        const parentId = evt.to.getAttribute("data-parent")
        fetch(self.data.get("update-url"), {
          method: "PUT",
          headers: {
            Accept: "application/json, text/javascript",
            "Content-Type": "application/json",
            "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
          },
          body: JSON.stringify({
            id: contentId,
            to: parentId,
            index: evt.newIndex,
          }),
        }).then((response) => {
          response.json().then(function (data) {
            let changeEvent = new CustomEvent("content-editor.changed", {
              bubbles: true,
              cancelable: true,
              detail: {
                contentId: data.content.id,
                path: data.content.path,
                fullPath: data.content.full_path,
              },
            })
            self.element.dispatchEvent(changeEvent)
          })
        })
      },
      onMove: function (evt) {
        const parentId = evt.to.getAttribute("data-parent")
        if (parentId) {
          const file = document.querySelector('[data-content="' + parentId + '"]').classList.contains("file")
          if (file) {
            evt.preventDefault()
            return false
          }
        }
      },
    })
  }
}