import elementLocale from 'element-ui/lib/locale'
import enElement from 'element-ui/lib/locale/lang/en'
import zhElement from 'element-ui/lib/locale/lang/zh-CN'

const DEFAULT_LOCALE = 'en'
const MESSAGE_FIELDS = ['name', 'label', 'title', 'help', 'msg', 'text', 'placeholder', 'content']
const TIMEZONE_LOCALE_MAP = {
  zh: [
    'Asia/Shanghai',
    'Asia/Urumqi',
    'Asia/Chongqing',
    'Asia/Harbin',
    'Asia/Hong_Kong',
    'Asia/Macau',
    'Asia/Taipei'
  ],
  bn: [
    'Asia/Dhaka'
  ],
  tr: [
    'Europe/Istanbul'
  ],
  ar: [
    'Asia/Riyadh',
    'Asia/Dubai',
    'Asia/Kuwait',
    'Asia/Qatar',
    'Asia/Bahrain',
    'Asia/Muscat',
    'Asia/Aden',
    'Asia/Baghdad',
    'Asia/Damascus',
    'Asia/Amman',
    'Asia/Beirut',
    'Asia/Jerusalem',
    'Africa/Cairo',
    'Africa/Tripoli',
    'Africa/Tunis',
    'Africa/Algiers',
    'Africa/Casablanca',
    'Africa/Khartoum'
  ]
}
let activeStore = null
const elementLocales = {
  en: enElement,
  zh: zhElement
}
const messageContext = require.context('./messages', false, /\.js$/)
const messages = messageContext.keys().reduce((result, filePath) => {
  const locale = filePath.replace('./', '').replace(/\.js$/, '')
  const localeModule = messageContext(filePath)
  result[locale] = localeModule.default || localeModule
  return result
}, {})
const localeOptions = Object.keys(messages)
  .sort((left, right) => {
    if (left === DEFAULT_LOCALE) {
      return -1
    }
    if (right === DEFAULT_LOCALE) {
      return 1
    }
    return left.localeCompare(right)
  })
  .map(locale => {
    const meta = messages[locale] && messages[locale].meta ? messages[locale].meta : {}
    const fallbackLabel = locale.toUpperCase()
    return {
      value: locale,
      label: meta.label || fallbackLabel,
      nativeLabel: meta.nativeLabel || meta.label || fallbackLabel
    }
  })

function normalizeLocale(locale) {
  return Object.prototype.hasOwnProperty.call(messages, locale) ? locale : DEFAULT_LOCALE
}

export function getLocaleOptions() {
  return localeOptions.slice()
}

export function getCurrentTimeZone() {
  try {
    return new Intl.DateTimeFormat().resolvedOptions().timeZone || ''
  } catch (error) {
    return ''
  }
}

export function detectLocaleByTimeZone(timeZone = getCurrentTimeZone()) {
  if (!timeZone) {
    return DEFAULT_LOCALE
  }

  const matchedLocale = Object.keys(TIMEZONE_LOCALE_MAP).find(locale => {
    if (!Object.prototype.hasOwnProperty.call(messages, locale)) {
      return false
    }

    return TIMEZONE_LOCALE_MAP[locale].some(pattern => {
      return timeZone === pattern || timeZone.indexOf(`${pattern}/`) === 0
    })
  })

  return normalizeLocale(matchedLocale || DEFAULT_LOCALE)
}

function getMessage(locale, key) {
  if (!key || !messages[locale]) {
    return undefined
  }

  const localeMessages = messages[locale]
  if (Object.prototype.hasOwnProperty.call(localeMessages, key)) {
    return localeMessages[key]
  }

  return key.split('.').reduce((result, current) => {
    if (result == null) {
      return undefined
    }
    return result[current]
  }, localeMessages)
}

export function getLocale() {
  if (!activeStore || !activeStore.state || !activeStore.state.app) {
    return DEFAULT_LOCALE
  }
  return normalizeLocale(activeStore.state.app.language)
}

export function translate(key, params) {
  if (typeof key !== 'string' || key.length === 0) {
    return key
  }

  const locale = getLocale()
  const localeText = getMessage(locale, key)
  let text = localeText

  if (text == null) {
    if (locale === 'zh') {
      text = key
    } else {
      text = getMessage(DEFAULT_LOCALE, key)
    }
  }

  if (text == null) {
    text = key
  }

  if (!params || typeof text !== 'string') {
    return text
  }

  return Object.keys(params).reduce((result, name) => {
    return result.replace(new RegExp(`\\{${name}\\}`, 'g'), params[name])
  }, text)
}

export function translateRouteTitle(title) {
  return translate(title)
}

function bindField(target, field) {
  if (!target || !Object.prototype.hasOwnProperty.call(target, field)) {
    return
  }

  const descriptor = Object.getOwnPropertyDescriptor(target, field)
  if (!descriptor || descriptor.get || typeof descriptor.value !== 'string') {
    return
  }

  const cacheKey = `__i18n_${field}`
  Object.defineProperty(target, cacheKey, {
    value: descriptor.value,
    enumerable: false,
    configurable: true,
    writable: true
  })

  Object.defineProperty(target, field, {
    enumerable: true,
    configurable: true,
    get() {
      return translate(this[cacheKey])
    },
    set(value) {
      this[cacheKey] = value
    }
  })
}

export function localizeCollection(collection, fields = MESSAGE_FIELDS, weakSet = new WeakSet()) {
  if (!collection || typeof collection !== 'object' || weakSet.has(collection)) {
    return collection
  }

  weakSet.add(collection)

  if (Array.isArray(collection)) {
    collection.forEach(item => localizeCollection(item, fields, weakSet))
    return collection
  }

  fields.forEach(field => bindField(collection, field))

  Object.keys(collection).forEach(key => {
    localizeCollection(collection[key], fields, weakSet)
  })

  return collection
}

export function setLocale(locale) {
  const nextLocale = normalizeLocale(locale)
  if (activeStore && activeStore.state && activeStore.state.app && activeStore.state.app.language !== nextLocale) {
    activeStore.dispatch('app/setLanguage', nextLocale)
  }
  elementLocale.use(elementLocales[nextLocale] || elementLocales[DEFAULT_LOCALE])
  return nextLocale
}

export function attachStore(store) {
  activeStore = store
}

const i18nPlugin = {
  install(Vue) {
    Vue.prototype.$t = function(key, params) {
      return translate(key, params)
    }
    Vue.prototype.$setLocale = function(locale) {
      return setLocale(locale)
    }
    Object.defineProperty(Vue.prototype, '$locale', {
      get() {
        return getLocale()
      }
    })
  }
}

export default i18nPlugin
