<!--
Liste déroulante avec autocompletion, à placer dans un composant `UiForm`

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

<template>
  <main>
    <!--suppress TypeScriptValidateTypes -->
    <v-autocomplete
        :model-value="modelValue"
        autocomplete="search"
        :items="items"
        :label="$t(fieldLabel)"
        :item-title="itemTitle"
        :item-value="itemValue"
        :no-filter="noFilter"
        :auto-select-first="autoSelectFirst"
        :multiple="multiple"
        :loading="isLoading"
        :return-object="returnObject"
        :search-input.sync="search"
        :prepend-icon="prependIcon"
        :error="error || !!fieldViolations"
        :error-messages="errorMessage || fieldViolations ? $t(fieldViolations) : ''"
        :rules="rules"
        :chips="chips"
        :hide-no-data="hideNoData"
        :no-data-text="isLoading ? $t('please_wait') : $t('no_result_matching_your_request')"
        @update:model-value="onUpdate"
        @update:search="emit('update:search', $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 {computed, ComputedRef, Ref} from "@vue/reactivity";
import {useFieldViolation} from "~/composables/form/useFieldViolation";
import ObjectUtils from "~/services/utils/objectUtils";
import {AnyJson} from "~/types/data";
import {PropType} from "@vue/runtime-core";

const props = defineProps({
  /**
   * v-model
   */
  modelValue: {
    type: [String, Number, Object, Array],
    required: false,
    default: null
  },
  /**
   * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
   * - Utilisé par la validation
   * - Laisser null si le champ ne s'applique pas à une entité
   */
  field: {
    type: String,
    required: false,
    default: null
  },
  /**
   * Label du champ
   * Si non défini, c'est le nom de propriété qui est utilisé
   */
  label: {
    type: String,
    required: false,
    default: null
  },
  /**
   * Liste des éléments de la liste
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-items
   */
  items: {
    type: Array as PropType<Array<Object>>,
    required: false,
    default: () => []
  },
  /**
   * Définit si le champ est en lecture seule
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-readonly
   */
  readonly: {
    type: Boolean,
    required: false
  },
  /**
   * Le model est l'objet lui-même, et non pas son id (ou la propriété définie avec itemValue)
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-return-object
   */
  returnObject: {
    type: Boolean,
    default: false
  },
  /**
   * Autorise la sélection multiple
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-multiple
   */
  multiple: {
    type: Boolean,
    default: false
  },
  /**
   * Propriété de l'objet à utiliser comme label
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-item-title
   */
  itemTitle: {
    type: String,
    default: 'title'
  },
  /**
   * Propriété de l'objet à utiliser comme clé (et correspondant au v-model)
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-item-value
   */
  itemValue: {
    type: String,
    default: 'id'
  },
  /**
   * Icône de gauche
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-prepend-icon
   */
  prependIcon: {
    type: String
  },
  /**
   * Rends les résultats sous forme de puces
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-chips
   */
  chips: {
    type: Boolean,
    default: false
  },
  /**
   * Le contenu de la liste est en cours de chargement
   */
  isLoading: {
    type: Boolean,
    required: false,
    default: false
  },
  /**
   * Propriété de l'objet utilisé pour grouper les items ; laisser null pour ne pas grouper
   */
  group: {
    type: String,
    required: false,
    default: null
  },
  /**
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-hide-no-data
   */
  hideNoData: {
    type: Boolean,
    required: false,
    default: false
  },
  // TODO: c'est quoi?
  slotText: {
    type: Array,
    required: false,
    default: null
  },
  /**
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-no-filter
   */
  noFilter: {
    type: Boolean,
    default: false
  },
  /**
   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-auto-select-first
   */
  autoSelectFirst: {
    type: Boolean,
    default: true
  },
  // TODO: c'est quoi?
  translate: {
    type: Boolean,
    default: false
  },
  /**
   * Règles de validation
   * @see https://vuetify.cn/en/components/forms/#validation-with-submit-clear
   */
  rules: {
    type: Array,
    required: false,
    default: () => []
  },
  /**
   * Le champ est-il actuellement en état d'erreur
   */
  error: {
    type: Boolean,
    required: false
  },
  /**
   * Si le champ est en état d'erreur, quel est le message d'erreur?
   */
  errorMessage: {
    type: String,
    required: false,
    default: null
  }
})

const i18n = useI18n()

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

const fieldLabel: string = props.label ?? props.field

const {fieldViolations, updateViolationState} = useFieldViolation(props.field)

const emit = defineEmits(['update:model-value', 'update:search'])

const onUpdate = (event: string) => {
  updateViolationState(event)
  emit('update:model-value', event)
}

/**
 * Items à afficher
 * TODO: à revoir
 */
const items: ComputedRef<Array<AnyJson>> = computed(() => {
  let _items: Array<any> = props.items
  return _items
  // if (props.group !== null) {
  //   _items = groupItems(props.items)
  // }
  //
  // return prepareGroups(_items)
})

/**
 * On construit l'Array à double entrée contenant les groups (headers) et les items
 * TODO: à revoir
 *
 * @param items
 */
const groupItems = (items: Array<any>): Array<Array<string>> => {
  const group = props.group as string | null
  if (group === null) {
    return items
  }

  const itemsByGroup: Array<Array<string>> = []
  let groupValue = null

  for (const item of items) {
    if (item) {
      groupValue = item[group]

      if (!itemsByGroup[groupValue]) {
        itemsByGroup[groupValue] = []
      }

      itemsByGroup[groupValue].push(item)
    }
  }

  return itemsByGroup
}

/**
 * Construction de l'Array JSON contenant toutes les propositions à afficher dans le select
 * TODO: à revoir
 *
 * @param groupedItems
 */
const prepareGroups = (groupedItems: Array<Array<string>>): Array<AnyJson> => {
  let finalItems: Array<AnyJson> = []

  for (const group in groupedItems) {

    // 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(groupedItems[group].map((item: any) => {
      return prepareItem(item)
    }))
  }
  return finalItems
}

/**
 * Construction d'un item
 * TODO: à revoir
 *
 * @param item
 */
const prepareItem = (item: Object): AnyJson => {
  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.itemTitle) {
    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(' ') })
}

</script>
