<template>
  <Story title="Input/Autocomplete">
    <div class="grid grid-cols-1 gap-6 md:grid-cols-2">
      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Simple (statique)</h2>
        <MalioInputAutocomplete
          v-model="simpleValue"
          label="Pays"
          :options="staticOptions"
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Avec icône à gauche</h2>
        <MalioInputAutocomplete
          v-model="leftIconValue"
          label="Recherche"
          icon-name="mdi:magnify"
          icon-position="left"
          :options="staticOptions"
        />
      </div>

      <div class="rounded-lg border p-4 md:col-span-2">
        <h2 class="mb-4 text-xl font-bold">Branché sur une API (simulé)</h2>
        <p class="mb-3 text-sm text-m-muted">
          Tapez au moins 2 caractères. Le parent écoute <code>@search</code> et alimente <code>options</code> + <code>loading</code>.
        </p>
        <MalioInputAutocomplete
          v-model="apiValue"
          label="Client"
          :options="apiOptions"
          :loading="apiLoading"
          :min-search-length="2"
          icon-name="mdi:magnify"
          icon-position="left"
          @search="onSearchApi"
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Création libre (allowCreate)</h2>
        <MalioInputAutocomplete
          v-model="createValue"
          label="Catégorie"
          :options="staticOptions"
          allow-create
          hint="Taper Entrée pour créer une nouvelle valeur"
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Désactivé</h2>
        <MalioInputAutocomplete
          v-model="disabledValue"
          label="Pays"
          :options="staticOptions"
          disabled
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Readonly</h2>
        <MalioInputAutocomplete
          v-model="readonlyValue"
          label="Pays"
          :options="staticOptions"
          readonly
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Avec hint</h2>
        <MalioInputAutocomplete
          v-model="hintValue"
          label="Pays"
          :options="staticOptions"
          hint="Sélectionne un pays dans la liste"
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Erreur</h2>
        <MalioInputAutocomplete
          v-model="errorValue"
          label="Pays"
          :options="staticOptions"
          error="Sélection invalide"
        />
      </div>

      <div class="rounded-lg border p-4">
        <h2 class="mb-4 text-xl font-bold">Succès</h2>
        <MalioInputAutocomplete
          v-model="successValue"
          label="Pays"
          :options="staticOptions"
          success="Sélection valide"
        />
      </div>
    </div>
  </Story>
</template>

<docs lang="md">
# MalioInputAutocomplete

Champ de saisie assistée (typeahead / combobox) : l'utilisateur tape pour filtrer une liste d'options, ou pour déclencher une recherche côté parent (API). Combine le pattern floating-label des autres inputs avec un dropdown inspiré de `MalioSelect`. Conçu pour les cas ERP où la liste vient d'un appel API (auth, transformation, cache gérés par le parent).

------------------------------------------------------------------------

## Props détaillées

### id
- Type: string
- Description: Identifiant HTML de l'input. Auto-généré si non fourni (préfixe `malio-input-autocomplete-`).

### label
- Type: string
- Description: Texte affiché comme label flottant.

### name
- Type: string
- Description: Attribut name de l'input (formulaires).

### modelValue
- Type: `string | number | null | undefined`
- Description: Valeur sélectionnée. Doit correspondre à un `option.value` (ou être un texte libre si `allowCreate`).

### options
- Type: `{ label: string; value: string | number }[]`
- Défaut: `[]`
- Description: Liste affichée dans le dropdown. Le parent alimente cette liste (statique ou via API en réponse à l'event `search`).

### loading
- Type: boolean
- Défaut: `false`
- Description: Affiche un spinner à la place du chevron et un message dans le dropdown.

### debounce
- Type: number
- Défaut: `300`
- Description: Délai (ms) avant émission de l'event `search` après une frappe. Évite de spammer l'API.

### minSearchLength
- Type: number
- Défaut: `0`
- Description: Nombre minimum de caractères avant d'émettre `search`. Pratique pour API : ne pas appeler avec query vide.

### allowCreate
- Type: boolean
- Défaut: `false`
- Description: Si vrai, l'utilisateur peut valider (Entrée ou clic ailleurs) une valeur libre non présente dans `options` ; émet l'event `create`.

------------------------------------------------------------------------

## Icône

### iconName
- Type: string
- Défaut: `''` (aucune)
- Description: Nom Iconify de l'icône décorative.

### iconPosition
- Type: `'left' | 'right'`
- Défaut: `left`
- Description: Côté d'affichage de l'icône. À droite, l'icône s'aligne avec le chevron.

### iconSize / iconColor
- Type: number / string
- Défaut: `24` / `text-m-muted`

------------------------------------------------------------------------

## Textes du dropdown

### noResultsText
- Type: string
- Défaut: `Aucun résultat`
- Description: Affiché quand `options` est vide.

### loadingText
- Type: string
- Défaut: `Chargement…`
- Description: Affiché pendant que `loading=true`.

### minSearchText
- Type: string
- Défaut: `Tapez pour rechercher`
- Description: Affiché quand l'utilisateur n'a pas atteint `minSearchLength`.

------------------------------------------------------------------------

## Apparence & Style

### inputClass / labelClass / groupClass
- Type: string
- Description: Classes CSS appliquées respectivement à l'input, au label et au conteneur (fusionnées via `twMerge`).

------------------------------------------------------------------------

## Validation & Contraintes

### required / disabled / readonly
- Type: boolean
- Description: Attributs HTML standards. `disabled` et `readonly` empêchent l'ouverture du dropdown.

------------------------------------------------------------------------

## États & Messages

### hint / error / success
- Type: string
- Description: Messages affichés sous le champ. `error` est prioritaire et active `aria-invalid`.

------------------------------------------------------------------------

## Clavier

- `↓` / `↑` : naviguer dans les options
- `Entrée` : sélectionner l'option active (ou créer si `allowCreate`)
- `Échap` : fermer le dropdown et revenir à la dernière sélection

------------------------------------------------------------------------

## Accessibilité

- `role="combobox"` sur l'input avec `aria-expanded`, `aria-controls`, `aria-activedescendant`.
- `role="listbox"` sur le dropdown, `role="option"` sur chaque entrée, `aria-selected` reflète `modelValue`.
- `aria-invalid` activé si `error` existe.
- `aria-describedby` référence dynamiquement le message affiché.

------------------------------------------------------------------------

## Events

### update:modelValue
- Émis quand l'utilisateur sélectionne une option. Permet l'utilisation avec v-model.

### search
- Émis (après debounce + minSearchLength) avec la query texte tapée. C'est ce que le parent écoute pour lancer l'appel API.

### select
- Émis avec l'objet `Option` complet (ou `null` à la réinitialisation). Utile pour récupérer le `label` côté parent en plus du `value`.

### create
- Émis avec la chaîne saisie quand `allowCreate` est vrai et que l'utilisateur valide une valeur libre.
</docs>

<script setup lang="ts">
import {ref} from 'vue'
import MalioInputAutocomplete from '../../components/malio/input/InputAutocomplete.vue'

type Option = {label: string; value: string | number}

const staticOptions: Option[] = [
  {label: 'France', value: 'fr'},
  {label: 'Belgique', value: 'be'},
  {label: 'Canada', value: 'ca'},
  {label: 'Suisse', value: 'ch'},
  {label: 'Luxembourg', value: 'lu'},
  {label: 'Allemagne', value: 'de'},
]

const simpleValue = ref<string | number | null>('fr')
const leftIconValue = ref<string | number | null>(null)
const createValue = ref<string | number | null>(null)
const disabledValue = ref<string | number | null>('fr')
const readonlyValue = ref<string | number | null>('be')
const hintValue = ref<string | number | null>(null)
const errorValue = ref<string | number | null>('fr')
const successValue = ref<string | number | null>('fr')

const apiValue = ref<string | number | null>(null)
const apiOptions = ref<Option[]>([])
const apiLoading = ref(false)

const fakeClients: Option[] = [
  {label: 'Yuno Malio', value: 1},
  {label: 'Yuna Corp', value: 2},
  {label: 'Yum Foods', value: 3},
  {label: 'Acme Inc.', value: 4},
  {label: 'Globex Corp', value: 5},
]

const onSearchApi = async (query: string) => {
  apiLoading.value = true
  await new Promise(resolve => setTimeout(resolve, 400))
  apiOptions.value = fakeClients.filter(c =>
    c.label.toLowerCase().includes(query.toLowerCase()),
  )
  apiLoading.value = false
}
</script>
