<template>
  <div>
    <div
      ref="root"
      :class="mergedGroupClass"
    >
    <button
      :id="buttonId"
      type="button"
      class="grow-height peer relative w-full border bg-white pl-3 pr-10 py-1 text-left outline-none focus-visible:border-m-primary"
      :class="[
          hasError
            ? isOpen
              ? openDirection === 'down'
                ? 'rounded-b-none !border !border-m-danger !border-b-0'
                : 'rounded-t-none !border !border-m-danger !border-t-0'
              : 'border-m-danger'
            : hasSuccess
              ? isOpen
                ? openDirection === 'down'
                  ? 'rounded-b-none !border !border-m-success !border-b-0'
                  : 'rounded-t-none !border !border-m-success !border-t-0'
                : 'border-m-success'
              : isOpen
                ? openDirection === 'down'
                  ? 'rounded-b-none !border !border-m-primary !border-b-0'
                  : 'rounded-t-none !border !border-m-primary !border-t-0'
                : isOptionSelected
                  ? 'border-black'
                  : 'border-m-muted',
          disabled ? 'cursor-not-allowed border-m-muted text-black/60' : 'cursor-pointer',
          label ? 'min-h-[40px]' : 'h-[40px] py-0',
          rounded,
          textField,
  ]"
      :aria-expanded="isOpen"
      :aria-controls="listboxId"
      :aria-invalid="hasError"
      :aria-describedby="describedBy"
      :disabled="disabled"
      @click="toggle"
    >
      <label
        v-if="label"
        class="floating-label pointer-events-none absolute left-3 inline-block origin-left transition-transform duration-150 font-medium"
        :class="[
          isOpen ? 'top-2  z-30' : 'top-2',
          hasError
            ? 'text-m-danger'
            : hasSuccess
              ? 'text-m-success'
              : isOpen
                ? 'text-m-primary'
                : isOptionSelected
                  ? 'text-black'
                  : 'text-m-muted',
          textLabel,
        ]"
        :style="labelTransformStyle"
      >
        {{ label }}
      </label>

      <div
        v-if="displayTags && selectedOptions.length > 0"
        class="flex flex-wrap items-center justify-start gap-1"
        :class="[label ? 'pt-1' : '']"
      >
        <span
          v-for="option in selectedOptions"
          :key="String(option.value)"
          class="inline-flex max-w-full items-center rounded-md border border-black px-2 text-sm leading-none text-black"
        >
          <span class="truncate pb-[2px]">{{ option.label }}</span>
        </span>
      </div>

      <span
        v-else-if="displayTag && emptyOptionLabel"
        class="block truncate text-right"
        :class="[
          textValue,
          label ? 'pl-24' : '',
          'text-m-muted'
        ]"
      >
        {{ emptyOptionLabel }}
      </span>

      <span
        v-if="!displayTag"
        class="block truncate text-right"
        :class="[
          textValue,
          label ? 'pl-24' : '',
          isOptionSelected ? 'text-black' : 'text-m-muted'
        ]"
      >
        {{ selectionSummary }}
      </span>

      <span
        class="absolute right-3 top-1/2 -translate-y-1/2"
        :class="[
          hasError
            ? 'text-m-danger'
            : hasSuccess
              ? 'text-m-success'
              : 'text-current'
        ]"
      >
      <slot name="icon">
        <IconifyIcon
          icon="mdi:chevron-down"
          width="20"
          class="transition-transform duration-300"
          :class="isOpen ? 'rotate-180' : 'rotate-0'"
        />
      </slot>
    </span>
    </button>

    <ul
      v-if="isOpen"
      :id="listboxId"
      ref="listRef"
      role="listbox"
      :aria-labelledby="buttonId"
      class="absolute left-0 right-0 z-20 max-h-60 w-full overflow-auto border bg-white"
      :class="[
        openDirection === 'down'
          ? 'top-[calc(100%-4px)] rounded-b-md border-t-0'
          : 'bottom-[calc(100%-4px)] rounded-t-md border-b-0',
        hasError
          ? 'select-scrollbar-error'
          : hasSuccess
            ? 'select-scrollbar-success'
            : 'select-scrollbar-primary',
        hasError
          ? 'border-m-danger'
          : hasSuccess
            ? 'border-m-success'
            : 'border-m-primary'
      ]"
    >
      <li
        v-if="displaySelectAll"
        class="border-b border-m-muted/30 px-3 py-2"
        @mousedown.prevent
      >
        <Checkbox
          :model-value="allSelected"
          :label="selectAllLabel"
          :disabled="disabled"
          group-class="!mt-0"
          label-class="option-checkbox w-full cursor-pointer font-semibold"
          tabindex="-1"
          @update:model-value="toggleAll"
        />
      </li>
      <li
        v-for="(opt, index) in normalizedOptions"
        :id="optionId(index)"
        :key="String(opt.value)"
        role="option"
        :aria-selected="isChecked(opt.value)"
        class="px-3 py-2"
        :class="[
            index === activeIndex ? 'bg-m-muted/10' : '',
            isChecked(opt.value) ? 'bg-m-muted/10 font-semibold' : '',
            'text-black'
          ]"
        @mouseenter="activeIndex = index"
        @mousedown.prevent
      >
        <Checkbox
          :model-value="isChecked(opt.value)"
          :label="opt.label || '\u00A0'"
          :disabled="disabled"
          group-class="!mt-0"
          label-class="option-checkbox w-full cursor-pointer"
          tabindex="-1"
          @update:model-value="toggleOption(opt.value)"
        />
      </li>
    </ul>
    </div>
    <p
      v-if="hint || hasError || hasSuccess"
      :id="`${buttonId}-describedby`"
      :class="[
        hasError
          ? 'text-m-danger'
          : hasSuccess
            ? 'text-m-success'
            : 'text-m-muted',
        'mt-1 ml-[2px] text-xs',
      ]"
    >
      {{ error || success || hint }}
    </p>
  </div>
</template>

<script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, useId, nextTick} from 'vue'
import {Icon as IconifyIcon} from '@iconify/vue'
import {twMerge} from 'tailwind-merge'
import Checkbox from '../checkbox/Checkbox.vue'

defineOptions({name: 'MalioSelectCheckbox', inheritAttrs: false})

type Option = {
  label: string;
  value: string | number
}
const props = withDefaults(defineProps<{
  modelValue: Array<string | number>
  options?: Option[]
  emptyOptionLabel?: string
  label?: string
  hint?: string
  error?: string
  success?: string
  textField?: string
  textValue?: string
  textLabel?: string
  rounded?: string
  displayTag?: boolean
  displaySelectAll?: boolean
  selectAllLabel?: string
  disabled?: boolean
  groupClass?: string
}>(), {
  options: () => [],
  emptyOptionLabel: '',
  label: '',
  hint: '',
  error: '',
  success: '',
  textField: 'text-lg',
  textValue: 'text-lg',
  textLabel: 'text-sm',
  rounded: 'rounded-md',
  displayTag: false,
  displaySelectAll: false,
  selectAllLabel: 'Tout sélectionner',
  disabled: false,
  groupClass: '',
})

const emit = defineEmits<{
  (e: 'update:modelValue', v: Array<string | number>): void
}>()
const root = ref<HTMLElement | null>(null)
const isOpen = ref(false)
const activeIndex = ref(-1)
const openDirection = ref<'down' | 'up'>('down')
const uid = useId()
const buttonId = `custom-select-btn-${uid}`
const listboxId = `custom-select-listbox-${uid}`
const listRef = ref<HTMLElement | null>(null)
const listHeight = ref(0)
const normalizedOptions = computed<Option[]>(() => props.options)
const mergedGroupClass = computed(() =>
  twMerge('relative w-full h-12 flex items-center', props.groupClass),
)
const hasError = computed(() => !!props.error)
const hasSuccess = computed(() => !!props.success && !hasError.value)
const isOptionSelected = computed(() =>
  props.modelValue.length > 0
)
const selectedOptions = computed(() =>
  normalizedOptions.value.filter(option => props.modelValue.includes(option.value)),
)
const displayTags = computed(() =>
  props.displayTag && selectedOptions.value.length > 0,
)
const shouldFloatLabel = computed(() =>
  isOpen.value || displayTags.value
)
const selectionSummary = computed(() =>
  `${props.modelValue.length}/${normalizedOptions.value.length}`
)
const allSelected = computed(() =>
  normalizedOptions.value.length > 0
  && normalizedOptions.value.every(opt => props.modelValue.includes(opt.value)),
)
const describedBy = computed(() =>
  (hasError.value || hasSuccess.value || !!props.hint) ? `${buttonId}-describedby` : undefined,
)

function optionId(index: number) {
  return `custom-select-opt-${uid}-${index}`
}

function updateOpenDirection() {
  if (!root.value) return

  const rect = root.value.getBoundingClientRect()
  const estimatedListHeight = Math.min(normalizedOptions.value.length * 40, 240)
  const spaceBelow = window.innerHeight - rect.bottom
  const spaceAbove = rect.top

  openDirection.value =
    spaceBelow >= estimatedListHeight || spaceBelow >= spaceAbove
      ? 'down'
      : 'up'
}

function open() {
  updateOpenDirection()
  isOpen.value = true

  const selectedIndex = normalizedOptions.value.findIndex(o => props.modelValue.includes(o.value))
  activeIndex.value = selectedIndex >= 0 ? selectedIndex : 0

  nextTick(() => {
    if (openDirection.value === 'up' && listRef.value) {
      listHeight.value = listRef.value.offsetHeight
    } else {
      listHeight.value = 0
    }
  })
}

const labelTransformStyle = computed(() => {
  // label non flottant
  if (!shouldFloatLabel.value) {
    return {}
  }

  // fermé ou ouverture vers le bas : comportement classique
  if (!isOpen.value || openDirection.value === 'down') {
    return {
      transform: 'translateY(-1.15rem) scale(0.9)',
    }
  }

  // ouverture vers le haut : on remonte en fonction de la hauteur de la liste
  const extraOffset = 8 // marge visuelle au-dessus de la liste en px
  const total = 4 +listHeight.value + extraOffset
  // 18 ≈ 1.15rem pour garder la même base que votre flottant actuel

  return {
    transform: `translateY(-${total}px) scale(0.9)`,
  }
})

function close() {
  isOpen.value = false
}

function toggle() {
  if (props.disabled) return
  if (isOpen.value) {
    close()
    return
  }
  open()
}

function isChecked(value: string | number) {
  return props.modelValue.includes(value)
}

function toggleOption(value: string | number) {
  if (isChecked(value)) {
    emit('update:modelValue', props.modelValue.filter(item => item !== value))
    return
  }
  emit('update:modelValue', [...props.modelValue, value])
}

function toggleAll() {
  if (allSelected.value) {
    emit('update:modelValue', [])
  } else {
    emit('update:modelValue', normalizedOptions.value.map(opt => opt.value))
  }
}

function onClickOutside(e: MouseEvent) {
  if (!root.value) return
  if (!root.value.contains(e.target as Node)) close()
}

onMounted(() => document.addEventListener('mousedown', onClickOutside))
onBeforeUnmount(() => document.removeEventListener('mousedown', onClickOutside))
</script>

<style scoped>
.floating-label {
  background: white;
  padding: 0 0.25rem;
}

:deep(ul[role="listbox"]) {
  scrollbar-width: auto;
  scrollbar-gutter: stable;
}

:deep(.select-scrollbar-primary) {
  scrollbar-color: rgb(var(--m-primary)) transparent;
}

:deep(.select-scrollbar-error) {
  scrollbar-color: #000000 transparent;
}

:deep(.select-scrollbar-success) {
  scrollbar-color: #000000 transparent;
}

</style>
