import MessageFormat from 'messageformat'
import { parse } from 'messageformat-parser'

export default {
  name: 'ICU',
  functional: true,
  props: {
    path: {
      type: String,
      default: null
    },
    tag: {
      type: String,
      default: 'span'
    },
    variables: {
      type: Object,
      default: () => ({})
    }
  },
  /**
   * @method render
   * @param {import('vue').CreateElement} h
   * @param {import('vue').RenderContext} context
   */
  render (h, { props, data, children, parent }) {
    // @ts-ignore
    const message = parent.$icu.getMessage(props.path, parent.$icu.locale)

    /** @type {any} */
    const options = {
      returnType: 'values',
      // @ts-ignore
      customFormatters: parent.$icu._customFormatters
    }

    // @ts-ignore
    const formatter = new MessageFormat(parent.$icu.locale, options)

    // @ts-ignore
    const fn = formatter.compile(message, parent.$icu.locale)

    /** @type {Array<import('messageformat-parser').Token>} */
    const parsedMessage = parse(message)
    /** @type {Array<import('messageformat-parser').Token>} */
    const sourceIndexes = []

    /**
     * For each variable specified in the props, we will loop through it
     * to find the "index" of each prop in the parsed values.
     */
    Object.keys(props.variables).forEach(attr => {
      /** @type {number} */
      const attributePosition = parsedMessage
        .findIndex(token => {
          if (typeof token === 'string') return false

          const tokenTypes = ['argument', 'function']
          return tokenTypes.includes(token.type) && token.arg && token.arg === attr
        })

      if (attributePosition !== -1) {
        sourceIndexes[attributePosition] = parsedMessage[attributePosition]
      }
    })

    const attributes = Object.keys(props.variables).map(key => ([
      key,
      props.variables[key]
    ]))

    /**
     * Since Object.fromEntries is not available here. Loop through the attributes to
     * create the object.
     */
    /** @type {any} */
    const objectAttributes = {}
    attributes.forEach(attr => {
      objectAttributes[attr[0]] = attr[1]
    })

    /** @type {any} */
    const messageFormatParts = fn({
      ...objectAttributes
    })

    /**
     * For each parts generated by the messageformat, we'll try to find the associated index
     * according to the loop made before. Accordingly, we'll use the scoped slot or just
     * render plain text.
     */
    // @ts-ignore
    const vNodes = messageFormatParts.map((part, index) => {
      /**
       * Check if the message part index belongs to the source indexes
       */
      if (sourceIndexes[index]) {
        // @ts-ignore
        const argName = sourceIndexes[index].arg

        if (data.scopedSlots && data.scopedSlots[argName]) {
          // Use scoped slot
          // @ts-ignore
          const slotVNodes = data.scopedSlots[argName]({
            value: props.variables[argName],
            key: (typeof part === 'string' || typeof part === 'number')
              ? part
              : part.join('')
          })

          return slotVNodes
        }
      }

      return part // Return plain text, we could eventually return a vNode here
    })

    return h(props.tag, data, [
      ...(children || []),
      ...vNodes
    ])
  }
}
