import { parseHookProps } from "../lib/attribute";
import { globalPubsub } from "../lib/pubsub";
import { smoothlyScrollToElement } from "../lib/utils";

/**
 * A hook managing notebook/section headline.
 *
 * Similarly to cells the headline is focus/insert enabled.
 *
 * ## Props
 *
 *   * `id` - an identifier for the focus/insert navigation
 *
 *   * `on-value-change` - name of the event pushed when the user
 *     edits heading value
 *
 *   * `metadata` - additional value to send with the change event
 *
 */
const Headline = {
  mounted() {
    this.props = this.getProps();

    this.isFocused = false;
    this.insertMode = false;

    this.initializeHeadingEl();

    this.navigationSubscription = globalPubsub.subscribe(
      "navigation",
      this.handleNavigationEvent.bind(this),
    );
  },

  updated() {
    this.props = this.getProps();
    this.initializeHeadingEl();
  },

  destroyed() {
    this.navigationSubscription.destroy();
  },

  getProps() {
    return parseHookProps(this.el, ["id", "on-value-change", "metadata"]);
  },

  initializeHeadingEl() {
    const headingEl = this.el.querySelector(`[data-el-heading]`);

    if (headingEl === this.headingEl) {
      return;
    }

    this.headingEl = headingEl;

    // Make sure only plain text is pasted
    this.headingEl.addEventListener("paste", (event) => {
      event.preventDefault();
      const text = event.clipboardData.getData("text/plain").replace("\n", " ");
      document.execCommand("insertText", false, text);
    });

    // Ignore enter
    this.headingEl.addEventListener("keydown", (event) => {
      if (event.key === "Enter") {
        event.preventDefault();
      }
    });

    this.headingEl.addEventListener("blur", (event) => {
      // Wait for other handlers to complete and if still in insert
      // mode force focus
      setTimeout(() => {
        if (this.isFocused && this.insertMode) {
          this.headingEl.focus();
          moveSelectionToEnd(this.headingEl);
        }
      }, 0);
    });
  },

  handleNavigationEvent(event) {
    if (event.type === "element_focused") {
      this.handleElementFocused(event.focusableId, event.scroll);
    } else if (event.type === "insert_mode_changed") {
      this.handleInsertModeChanged(event.enabled);
    }
  },

  handleElementFocused(focusableId, scroll) {
    if (this.props.id === focusableId) {
      this.isFocused = true;
      this.el.setAttribute("data-js-focused", "");
      if (scroll) {
        smoothlyScrollToElement(this.el);
      }
    } else if (this.isFocused) {
      this.isFocused = false;
      this.el.removeAttribute("data-js-focused");
    }
  },

  handleInsertModeChanged(insertMode) {
    if (this.isFocused && !this.insertMode && insertMode) {
      this.insertMode = insertMode;
      // While in insert mode, ignore the incoming changes
      this.el.setAttribute("phx-update", "ignore");
      this.headingEl.setAttribute("contenteditable", "");
      this.headingEl.focus();
      moveSelectionToEnd(this.headingEl);
    } else if (this.insertMode && !insertMode) {
      this.insertMode = insertMode;
      this.headingEl.removeAttribute("contenteditable");
      this.el.removeAttribute("phx-update");
      this.pushEvent(this.props.onValueChange, {
        value: this.headingEl.textContent.trim(),
        metadata: this.props.metadata,
      });
    }
  },
};

function moveSelectionToEnd(element) {
  const range = document.createRange();
  range.selectNodeContents(element);
  range.collapse(false);
  const selection = window.getSelection();
  selection.removeAllRanges();
  selection.addRange(range);
}

export default Headline;
