<script setup>
import { computed, inject, onBeforeMount, ref } from "vue"
import { Combobox, ComboboxLabel, ComboboxOption, ComboboxOptions, ComboboxInput, ComboboxButton } from "@headlessui/vue";
import AisIcon from "@/components/icon/AisIcon.vue";
import { CONTROL_INVALID, controlProps } from "@/util/controls";
import { iconProps } from "@c/icon/ais-icons";
import { useSize } from "@/composables/useInjections";

const query = ref('')

const props = defineProps({
  modelValue: { type: [String, Object, Number], default: '' },
  options: { type: Array, default: () => [] },
  label: { type: String, default: undefined },
  placeholder: { type: String, default: '' },
  disabled: { type: Boolean, default: false },
  invalid: { type: Boolean, default: false },
  labelKey: { type: String, default: () => 'label' },
  valueKey: { type: String, default: () => 'value' },
  disabledKey: { type: String, default: () => 'disabled' },
  displayValue: { type: Function, required: false, default: undefined },
  checkOnRight: { type: Boolean, default: false },
  // ...transitionProps,
  ...controlProps,
  ...iconProps,
})

const emit = defineEmits([
  'update:modelValue',
  'load',
  'change'
])

const model = computed({
  get() { return props.modelValue },
  set(value) {
    emit('update:modelValue', value)
    selectedOption.value = value
    // console.log(`model.set: value=[${value}], typeof=[${typeof value}]`)
    emit('change', selectedObject.value)
  }
})

const size = useSize(props, 'md')

const injectedInvalid = inject(CONTROL_INVALID, false)
const isInvalid = computed(() => props.invalid || injectedInvalid.value)

const hasLeadingIcon = !!props.icon
const hasTrailingIcon = computed(() => !!props.status || isInvalid.value)

const trailingIcon = computed(() => isInvalid.value ? 'error' : props.status)

const selectedOption = ref(model.value)
const selectedObject = computed(() => {
  // console.log(`selectedObject: so=[${!!selectedOption.value}], typeof=[${typeof selectedOption.value}]`)
  if(!!selectedOption.value && typeof selectedOption.value === 'object')
    return selectedOption.value

  return props.options.find(option => selectedOption.value === getOptionValue(option))
})
const classes = computed(() => [size.value] )

const optionClass = computed(() => {
  const classes = ['relative cursor-default select-none py-2 text-base']

  if(props.checkOnRight)
    classes.push('pl-3 pr-9')
  else
    classes.push('pl-9 pr-3')

  return classes.join(' ')
})

const confirmClass = computed(() => {
  const classes = ['absolute inset-y-0 flex items-center text-secondary']

  if(props.checkOnRight)
    classes.push('right-0 pr-4')
  else
    classes.push('left-0 pl-2.5')

  return classes.join(' ')
})

const filteredOptions = computed(() => {
  // console.log(`filteredOptions: query=[${query.value}], options=[${props.options.length}]`)
  return query.value ? props.options.filter(option => getOptionLabel(option).toLowerCase().includes(query.value.toLowerCase())) : props.options
})

const getOptionLabel = (option) => {
  // console.log(`getOptionLabel: labelKey=[${props.labelKey}], hasOwn=[${option.hasOwnProperty(props.labelKey)}] option=[${JSON.stringify(option)}]`)
  if(!option) return ''
  if(typeof option === 'string') return option
  if(props.labelKey && option.hasOwnProperty(props.labelKey)) return option[props.labelKey]

  return option[Object.keys(option)[0]]
}
const getOptionValue = (option) => {
  if(!option) return ''
  if(typeof option === 'object' && option.hasOwnProperty(props.valueKey)) return option[props.valueKey]

  return option
}
const getDisabledValue = (option) => {
  if(!!option && typeof option === 'object' && option.hasOwnProperty(props.disabledKey)) return option[props.disabledKey]

  return false
}
const getDisplayValue = () => {
  if(!selectedObject.value) return selectedOption.value || model.value || ''
  if(props.displayValue) return props.displayValue(selectedObject.value)

  return getOptionLabel(selectedObject.value) || selectedOption.value
}

const handleQueryChange = ($event) => {
  query.value = $event.target.value
  if(!query.value)
    model.value = undefined
  else
    model.value = query.value
}

onBeforeMount(() => {
  emit('load', selectedObject.value)
})
</script>

<template>
  <Combobox as="div" v-model="model" :disabled="disabled">
    <ComboboxLabel v-if="label" class="block text-sm mb-1 font-medium text-gray-700">{{ label }}</ComboboxLabel>
    <div class="relative ais-input ais-control ais-combobox" :class="{ leadingIcon: hasLeadingIcon, invalid: isInvalid }">
      <div v-if="hasLeadingIcon" class="econ left">
        <AisIcon :icon="icon" />
      </div>

      <ComboboxInput class="" :class="classes" v-bind="$attrs"
                     :placeholder="placeholder"
                     :disabled="disabled"
                     :display-value="getDisplayValue"
                     @change="handleQueryChange($event)"
                     @focus="$event.target.select()"
      />

      <div v-if="hasTrailingIcon" class="econ right">
        <AisIcon :icon="trailingIcon" class="" />
      </div>

      <ComboboxButton class="absolute inset-y-0 right-0 flex items-center px-4 focus:outline-none" :class="{ 'cursor-not-allowed': disabled }">
        <AisIcon icon="down"/>
      </ComboboxButton>


      <ComboboxOptions as="div" class="absolute z-10 max-h-60 w-full overflow-auto rounded bg-gray-50 border border-ui-gray-100 py-1 text-base shadow-lg">
        <ComboboxOption v-for="option in filteredOptions"
                        :key="option"
                        :value="getOptionValue(option)"
                        :disabled="getDisabledValue(option)"
                        as="template"
                        v-slot="{ active, selected, disabled }"
        >
          <div :class="[optionClass, active ? 'bg-ui-gray-50' : '', disabled ? 'text-muted' : '']">
<!--          <div :class="[optionClass, active ? 'bg-secondary text-white' : 'text-secondary', disabled ? 'text-muted' : '']">-->
            <span v-if="selected && !checkOnRight" :class="confirmClass">
              <AisIcon icon="confirm"/>
            </span>

            <slot name="default" :option="option" :active="active" :selected="selected">
              {{ getOptionLabel(option) }}
            </slot>

            <span v-if="selected && checkOnRight" :class="[confirmClass, active ? 'text-primary' : 'text-secondary']">
              <AisIcon icon="confirm"/>
            </span>
          </div>
        </ComboboxOption>
      </ComboboxOptions>
    </div>
  </Combobox>
</template>
