import Polyglot from 'node-polyglot'
import { App, computed, reactive } from 'vue'

import { has } from '@/utilities'
import { range } from 'lodash'
import { parseISO } from 'date-fns'

const STORAGE_KEY = 'aura:locale'

const DEFAULT_LOCALE =
  localStorage.getItem(STORAGE_KEY) ?? import.meta.env.VUE_APP_DEFAULT_LOCALE ?? 'de'

const locales = Object.fromEntries(
  Object.entries(import.meta.glob('./??.js', { import: 'default', eager: true })).map(
    ([key, locale]) => [(/([a-z]+)?.[jt]s$/.exec(key) as string[])[1], locale],
  ),
)

function createPolyglot(locale: string) {
  return new Polyglot({ phrases: locales[locale] })
}

const state = reactive({
  locale: DEFAULT_LOCALE,
  polyglot: createPolyglot(DEFAULT_LOCALE),
})

const locale = computed({
  get() {
    return state.locale
  },
  set(newLocale) {
    updateLocale(newLocale)
  },
})
const availableLocales = computed(() => Object.keys(locales))

function t(phrase: string, context = {}): string {
  return state.polyglot.t(phrase, context)
}

// IMHO this is not the best naming for this function, but it is consistent
// with the vue-i18n library that most people working on vue applications
// will be familiar with.
function te(phrase: string): boolean {
  return state.polyglot.has(phrase)
}

function updateLocale(newLocale: string): void {
  if (has(locales, newLocale)) {
    state.locale = newLocale
    state.polyglot = createPolyglot(newLocale)
    localStorage.setItem(STORAGE_KEY, newLocale)
  } else {
    throw new Error(`Unknown locale code for language: ${newLocale}`)
  }
}

export function getLanguageName(languageCode: string) {
  return new Intl.DisplayNames([languageCode], { type: 'language' }).of(languageCode)
}

/**
 * Returns a list of weekday names starting on monday.
 * @param locale string
 */
export function getDayNames(locale: string) {
  return range(7).map((i) =>
    // January, 1st 2018 is a monday
    parseISO(`2018-01-0${i + 1}`).toLocaleString(locale, { weekday: 'long' }),
  )
}

export function getMonthNames(locale: string) {
  return range(12).map((i) =>
    parseISO(`2018-${(i + 1).toString().padStart(2, '0')}-01`).toLocaleString(locale, {
      month: 'long',
    }),
  )
}

const dayNames = computed(() => getDayNames(locale.value))
const monthNames = computed(() => getMonthNames(locale.value))

export function useI18n() {
  // This interface is intentionally similar to the one provided by
  // Vue I18n to make it easier for newcomers already familiar with it
  // and to ease a potential future migration.
  // See https://vue-i18n.intlify.dev/guide/advanced/composition.html#basic-usage
  return {
    t,
    te,
    locale,
    dayNames,
    monthNames,
    availableLocales,
  }
}

export const TranslationPlugin = {
  install(app: App) {
    app.config.globalProperties.$activeLocale = () => locale.value
    app.config.globalProperties.$locale = updateLocale
    app.config.globalProperties.$t = t
    app.config.globalProperties.$te = te
  },
}