const dayjs = require("dayjs")
dayjs.extend(require("dayjs/plugin/duration"))
dayjs.extend(require("dayjs/plugin/advancedFormat"))
dayjs.extend(require("dayjs/plugin/relativeTime"))
dayjs.extend(require("dayjs/plugin/utc"))
dayjs.extend(require("dayjs/plugin/timezone"))

/**
 * This file was largely taken from the helper-date package - we did this for two reasons:
 * 1. It made use of both moment of date.js - this caused some weird bugs with some relatively simple
 * syntax and didn't offer much in return.
 * 2. Replacing moment with dayjs helps massively reduce bundle size.
 * The original package can be found here:
 * https://github.com/helpers/helper-date
 */

function isOptions(val) {
  return typeof val === "object" && typeof val.hash === "object"
}

function isApp(thisArg) {
  return (
    typeof thisArg === "object" &&
    typeof thisArg.options === "object" &&
    typeof thisArg.app === "object"
  )
}

function getContext(thisArg, locals, options) {
  if (isOptions(thisArg)) {
    return getContext({}, locals, thisArg)
  }
  // ensure args are in the correct order
  if (isOptions(locals)) {
    return getContext(thisArg, options, locals)
  }
  const appContext = isApp(thisArg) ? thisArg.context : {}
  options = options || {}

  // if "options" is not handlebars options, merge it onto locals
  if (!isOptions(options)) {
    locals = Object.assign({}, locals, options)
  }
  // merge handlebars root data onto locals if specified on the hash
  if (isOptions(options) && options.hash.root === true) {
    locals = Object.assign({}, options.data.root, locals)
  }
  let context = Object.assign({}, appContext, locals, options.hash)
  if (!isApp(thisArg)) {
    context = Object.assign({}, thisArg, context)
  }
  if (isApp(thisArg) && thisArg.view && thisArg.view.data) {
    context = Object.assign({}, context, thisArg.view.data)
  }
  return context
}

function initialConfig(str, pattern, options) {
  if (isOptions(pattern)) {
    options = pattern
    pattern = null
  }

  if (isOptions(str)) {
    options = str
    pattern = null
    str = null
  }
  return { str, pattern, options }
}

function setLocale(str, pattern, options) {
  // if options is null then it'll get updated here
  const config = initialConfig(str, pattern, options)
  const defaults = { lang: "en", date: new Date(config.str) }
  // for now don't allow this to be configurable, don't pass in options
  const opts = getContext(this, defaults, {})

  // set the language to use
  dayjs.locale(opts.lang || opts.language)
}

module.exports.date = (str, pattern, options) => {
  const config = initialConfig(str, pattern, options)

  // if no args are passed, return a formatted date
  if (config.str == null && config.pattern == null) {
    dayjs.locale("en")
    return dayjs().format("MMMM DD, YYYY")
  }

  setLocale(config.str, config.pattern, config.options)

  let date = dayjs(new Date(config.str))
  if (typeof config.options === "string") {
    date =
      config.options.toLowerCase() === "utc"
        ? date.utc()
        : date.tz(config.options)
  } else {
    date = date.tz(dayjs.tz.guess())
  }
  if (config.pattern === "") {
    return date.toISOString()
  }
  return date.format(config.pattern)
}

module.exports.duration = (str, pattern, format) => {
  const config = initialConfig(str, pattern)

  setLocale(config.str, config.pattern)

  const duration = dayjs.duration(config.str, config.pattern)
  if (!isOptions(format)) {
    return duration.format(format)
  } else {
    return duration.humanize()
  }
}
