import Avvo from '../util/avvo-object'

const $ = global.jQuery

export class SmoothScroller {
  static get VERSION() {
    return '1.0.0'
  }

  static get DEFAULTS() {
    return Object.freeze({
      anchorSelector:         'a[href^="#"]',
      fastSpeed:              400,
      slowSpeed:              600,
      adjustScrollCoordinate: undefined, // callback fn(targetY, $target)
    })
  }


  constructor(el, options) {
    this.$element = $(el)
    this.$element.data('ui.smoothScroller', this)

    this.settings = $.extend({}, SmoothScroller.DEFAULTS, this.$element.data(), options)

    this.$element.on('click.ui.smoothScroller', this.settings.anchorSelector, event => this.onClick(event))
  }

  destroy() {
    this.$element.off('click.ui.smoothScroller')
    this.$element.removeData('ui.smoothScroller')
    Avvo.ui.smoothScroller = undefined
  }


  onClick(event) {
    if (!this.eventShouldTriggerScroll(event)) return
    event.preventDefault()

    this.scrollTo(event.currentTarget.getAttribute('href'))
  }

  // @param selector (String, DOM node, jQuery object)
  // @param scrollSpeed (Int)
  //   animation speed, in ms. Defaults to `slowSpeed` or `fastSpeed` depending
  //   on distance to target.
  // @return (Promise)
  scrollTo(selector, scrollSpeed) {
    const $target   = this.getScrollTarget(selector)
    const targetY   = this.getScrollCoordinate($target)
    const distance  = Math.abs($(window).scrollTop() - targetY)
    let speed = scrollSpeed

    if (typeof speed === 'undefined') {
      speed = (distance < window.screen.availHeight) ?
        this.settings.slowSpeed :
        this.settings.fastSpeed
    }

    const sharedEventData = { target: $target[0], targetY, distance, speed }

    this.$element.trigger('scrollstart.ui.smoothScroller', sharedEventData)


    return $('html,body')
      .stop()
      .animate({ scrollTop: targetY }, {
        duration: speed,
        easing: 'swing',
      })
      .promise()
      .done(() => {
        if (typeof selector === 'string' && selector.indexOf('#') === 0) {
          global.history.replaceState(null, null, selector)
        }

        this.$element.trigger('scrollend.ui.smoothScroller', sharedEventData)
      })
  }

  // @param selector String, DOM node, jQuery object
  // @return (Int) the desired Y coordinate to scroll to, in pixels
  getScrollCoordinate(selector) {
    const $target = $(selector)
    let targetY = $target.offset().top - parseInt($target.css('margin-top'), 10)
    if (this.settings.adjustScrollCoordinate) {
      targetY = this.settings.adjustScrollCoordinate(targetY, $target[0])
    }
    return targetY
  }

  // @param selector String, DOM node, jQuery object
  // @return (jQuery)
  getScrollTarget(selector) {
    if (selector === '#') return $('body')

    let $target = $(selector)
    if (!$target.length && selector.indexOf('#') === 0) {
      $target = $(`a[name='${selector.slice(1)}']`)
    }

    return $target
  }

  // @return (Bool) whether the event should trigger a smooth scroll
  eventShouldTriggerScroll(event) {
    const href = event.currentTarget.getAttribute('href')

    if (event.isDefaultPrevented()) return false
    if (href === '#') return true
    if (!document.querySelector(`[id='${href.slice(1)}'], a[name='${href.slice(1)}']`)) return false

    return true
  }
}

export function init() {
  if (Avvo.ui.smoothScroller) {
    if (window.console) {
      console.warn('Avvo.ui.smoothScroller already defined; canceling smoothScroller.init()') // eslint-disable-line
    }
    return
  }

  Avvo.ui.smoothScroller = new SmoothScroller(document)
}
