<!--
Liste déroulante avec autocompletion

@see https://vuetifyjs.com/en/components/autocompletes/#usage
-->

<template>
  <main>
    <v-autocomplete
      autocomplete="search"
      :value="data"
      :items="itemsToDisplayed"
      :label="$t(label_field)"
      item-text="itemTextDisplay"
      :item-value="itemValue"
      :no-data-text="$t('autocomplete_research')"
      :no-filter="noFilter"
      auto-select-first
      :multiple="multiple"
      :loading="isLoading"
      :return-object="returnObject"
      :search-input.sync="search"
      :prepend-icon="prependIcon"
      :error="error"
      :rules="rules"
      @input="onChange($event)"
    >
      <template v-if="slotText" v-slot:item="data">
        <v-list-item-content v-text="data.item.slotTextDisplay"></v-list-item-content>
      </template>
    </v-autocomplete>
  </main>
</template>

<script lang="ts">
import {computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted, useContext} from '@nuxtjs/composition-api'
import { AnyJson } from '~/types/interfaces'
import * as _ from 'lodash'
import {$objectProperties} from "~/services/utils/objectProperties";
import {$useError} from "~/use/form/useError";

export default defineComponent({
  props: {
    label: {
      type: String,
      required: false,
      default: null
    },
    field: {
      type: String,
      required: false,
      default: null
    },
    data: {
      type: [String, Number, Object, Array],
      required: false,
      default: null
    },
    items: {
      type: Array,
      required: false,
      default: () => []
    },
    readonly: {
      type: Boolean,
      required: false
    },
    itemValue: {
      type: String,
      default: 'id'
    },
    itemText: {
      type: Array,
      required: true
    },
    group:{
      type: String,
      required: false,
      default: null
    },
    slotText: {
      type: Array,
      required: false,
      default: null
    },
    returnObject: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    isLoading: {
      type: Boolean,
      default: false
    },
    noFilter: {
      type: Boolean,
      default: false
    },
    prependIcon: {
      type: String
    },
    translate: {
      type: Boolean,
      default: false
    },
    rules: {
      type: Array,
      required: false,
      default: () => []
    },
  },
  setup (props, { emit }) {
    const {app:{i18n}} = useContext()
    const search:Ref<string|null> = ref(null)
    const {error, onChange} = $useError(props.field, emit)

    // On reconstruit les items à afficher...
    const itemsToDisplayed: ComputedRef<Array<AnyJson>> = computed(() => {
      const itemsByGroup:Array<Array<string>> = classItemsByGroup(props.items)
      return prepareItemsToDisplayed(itemsByGroup)
    })

    const unwatch = watch(search, _.debounce(async (newResearch, oldResearch) => {
      if(newResearch !== oldResearch && oldResearch !== null)
        emit('research', newResearch)
    }, 500))

    onUnmounted(() => {
      unwatch()
    })


    /**
     * On construit l'Array à double entrée contenant les groups (headers) et les propositions
     * @param items
     */
    const classItemsByGroup = (items:Array<any>): Array<Array<string>> => {
      const itemsByGroup:Array<Array<string>> = []
      for (const item of items){
        if(item){
          if(!itemsByGroup[item[props.group as string]])
            itemsByGroup[item[props.group as string]] = []

          itemsByGroup[item[props.group as string]].push(item)
        }
      }
      return itemsByGroup
    }

    /**
     * Construction de l'Array JSON contenant toutes les propositions à afficher dans le select
     * @param itemsByGroup
     */
    const prepareItemsToDisplayed = (itemsByGroup:Array<Array<string>>):Array<AnyJson> => {
      let finalItems:Array<AnyJson> = []
      for(const group in itemsByGroup){

        //Si un group est présent, alors on créer le group options header
        if(group !== 'undefined'){
          finalItems.push({header: i18n.t(group as string)})
        }

        //On parcours les items pour préparer les texts/slotTexts à afficher
        finalItems = finalItems.concat(itemsByGroup[group].map((item: any) => {
          const slotTextDisplay: Array<string> = []
          const itemTextDisplay: Array<string> = []

          item = $objectProperties.cloneAndFlatten(item)

          //Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
          if(props.slotText){
            for (const text of props.slotText) {
              slotTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
            }
          }

          for (const text of props.itemText) {
            itemTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
          }

          //On reconstruit l'objet
          return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
        }))
      }
      return finalItems
    }

    return {
      label_field: props.label ?? props.field,
      itemsToDisplayed,
      search,
      error,
      onChange
    }
  }
})
</script>
