/**
 * --------------------------------------------------------------------------
 * hamburger.js
 * Hamburger extension to Bootstrap Collapse
 *
 * The extension ensures a hamburger icon is added to the trigger element and
 * manages its open/closed state
 * --------------------------------------------------------------------------
 */

import { defineJQueryPlugin } from 'bootstrap/js/src/util/index.js'
import EventHandler from 'bootstrap/js/src/dom/event-handler.js'
import SelectorEngine from 'bootstrap/js/src/dom/selector-engine.js'
import Collapse from 'bootstrap/js/src/collapse.js'
import MobileToggles from './mobile-toggles.js'

/**
 * Constants
 */

const NAME = 'hamburger'
const DATA_KEY = 'uoe.hamburger'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'

const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`

const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="hamburger"]'
const SELECTOR_COLLAPSE_DATA_TOGGLE = '[data-bs-toggle="collapse"]'
const SELECTOR_HAMBURGER = '.hamburger'
const CLASS_NAME_EXPANDED = 'hamburger-expanded'

// These constants are from Collapse and are needed to support us having to copy
// in show() since it directly references Collapse
const CLASS_NAME_COLLAPSE = 'collapse'
const CLASS_NAME_COLLAPSING = 'collapsing'
const CLASS_NAME_SHOW = 'show'
const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'
const EVENT_SHOW = 'show.bs.collapse'
const EVENT_SHOWN = 'shown.bs.collapse'

const EdGELVersion = '5.1.6-rc.2'

const Default = {
  toggle: true,
  parent: null
}

/**
 * Private methods
 */

function hamburgerIcon() {
  const icon = document.createElement('span')
  icon.className = 'hamburger'
  icon.setAttribute('aria-hidden', 'true')
  return icon
}

/**
 * Class definition
 */

class Hamburger extends Collapse {
  constructor(element, config) {
    super(element, config)
    // We have to override what Collapse found and use our SELECTOR_DATA_TOGGLE
    this._triggerArray.length = 0

    const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)

    for (const elem of toggleList) {
      const selector = SelectorEngine.getSelectorFromElement(elem)
      const filterElement = SelectorEngine.find(selector)
        .filter(foundElement => foundElement === this._element)

      if (selector !== null && filterElement.length) {
        this._triggerArray.push(elem)
      }
    }

    if (!this._config.parent) {
      this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())
    }
  }

  // Getters
  static get Default() {
    return Default
  }

  static get NAME() {
    return NAME
  }

  static get DATA_KEY() {
    return DATA_KEY
  }

  static get VERSION() {
    const bsVersion = super.VERSION
    return `${bsVersion}+edgel-${EdGELVersion}`
  }

  // Public
  show() {
    // TODO: Keep reviewing this: We are copying this whole method from Collapse :(
    //   Unfortunately it is explicitly calling the static Collapse.getOrCreateInstance
    //   on all children which then clashes with previously created Hamburgers - the
    //   Bootstrap code (rightly) doesn't like more than one instance per element.
    if (this._isTransitioning || this._isShown()) {
      return
    }

    let activeChildren = []

    // find active children
    if (this._config.parent) {
      activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)
        .filter(element => element !== this._element)
        .map(element => Hamburger.getOrCreateInstance(element, { toggle: false }))
    }

    if (activeChildren.length && activeChildren[0]._isTransitioning) {
      return
    }

    const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)
    if (startEvent.defaultPrevented) {
      return
    }

    for (const activeInstance of activeChildren) {
      activeInstance.hide()
    }

    const dimension = this._getDimension()

    this._element.classList.remove(CLASS_NAME_COLLAPSE)
    this._element.classList.add(CLASS_NAME_COLLAPSING)

    this._element.style[dimension] = 0

    this._addAriaAndCollapsedClass(this._triggerArray, true)
    this._isTransitioning = true

    const complete = () => {
      this._isTransitioning = false

      this._element.classList.remove(CLASS_NAME_COLLAPSING)
      this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)

      this._element.style[dimension] = ''

      EventHandler.trigger(this._element, EVENT_SHOWN)
    }

    const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
    const scrollSize = `scroll${capitalizedDimension}`

    this._queueCallback(complete, this._element, true)
    this._element.style[dimension] = `${this._element[scrollSize]}px`
  }

  // Private
  _getFirstLevelChildren(selector) {
    // Enforce a Hamburger selector
    const hamburgerSelector = selector === SELECTOR_COLLAPSE_DATA_TOGGLE ? SELECTOR_DATA_TOGGLE : selector
    return super._getFirstLevelChildren(hamburgerSelector)
  }

  _addAriaAndCollapsedClass(triggerArray, isOpen) {
    if (!triggerArray.length) {
      return
    }

    for (const element of triggerArray) {
      const hamburger = SelectorEngine.findOne(SELECTOR_HAMBURGER, element)
      if (hamburger) {
        if (isOpen) {
          hamburger.classList.add(CLASS_NAME_EXPANDED)
        } else {
          hamburger.classList.remove(CLASS_NAME_EXPANDED)
        }
      }
    }

    super._addAriaAndCollapsedClass(triggerArray, isOpen)
  }

  // Static
  static jQueryInterface(config) {
    const _config = {}
    if (typeof config === 'string' && /show|hide/.test(config)) {
      _config.toggle = false
    }

    return this.each(function () {
      const data = Hamburger.getOrCreateInstance(this, _config)

      if (typeof config === 'string') {
        if (data[config] === undefined) {
          throw new TypeError(`No method named "${config}"`)
        }

        data[config]()
      }
    })
  }
}

/**
 * Ensure hamburger icons are on all elements
 */
MobileToggles.addIcons(SELECTOR_DATA_TOGGLE, hamburgerIcon(), SELECTOR_HAMBURGER)

/**
 * Data API implementation
 */

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
  if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {
    event.preventDefault()
  }

  const selector = SelectorEngine.getSelectorFromElement(this)
  const selectorElements = SelectorEngine.find(selector)

  for (const element of selectorElements) {
    Hamburger.getOrCreateInstance(element, { toggle: false }).toggle()
  }
})

/**
 * jQuery
 */

defineJQueryPlugin(Hamburger)

export default Hamburger
