<!--
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(fieldLabel)"
      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 || !!violation"
      :error-messages="errorMessage || violation ? $t(violation) : ''"
      :rules="rules"
      :chips="chips"
      @input="onChange($event)"
    >
      <template v-if="slotText" #item="data">
        <v-list-item-content v-text="data.item.slotTextDisplay"></v-list-item-content>
      </template>
    </v-autocomplete>
  </main>
</template>

<script setup lang="ts">
import {useNuxtApp} from "#app";
import {computed, ComputedRef, Ref} from "@vue/reactivity";
import {useFieldViolation} from "~/composables/form/useFieldViolation";
import {AnyJson} from "~/types/enum/data";
import {onUnmounted, watch} from "@vue/runtime-core";
import ObjectUtils from "~/services/utils/objectUtils";

const props = defineProps({
  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: () => []
  },
  chips: {
    type: Boolean,
    default: false
  },
  error: {
    type: Boolean,
    required: false
  },
  errorMessage: {
    type: String,
    required: false,
    default: null
  }
})

const { emit } = useNuxtApp()

const { i18n } = useNuxtApp()

const search: Ref<string|null> = ref(null)

const fieldLabel = props.label ?? props.field

const { violation, onChange } = useFieldViolation(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,
    useDebounce(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 group = props.group as string
  const itemsByGroup: Array<Array<string>> = []

  for (const item of items) {
    if (item) {
      if (!itemsByGroup[item[group]]) {
        itemsByGroup[item[group]] = []
      }

      itemsByGroup[item[group]].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 groupe est présent, alors on créé le groupe options header
    if (group !== 'undefined') {
      finalItems.push({header: i18n.t(group as string)})
    }

    // On parcourt 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 = ObjectUtils.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
}
</script>
