<template>
  <div
    :class="[
      'form-item',
      half ? 'form-item-half' : '',
      elclass || '',
      $slots.button ? 'form-item-wb' : ''
    ]"
    :id="'formselect' + rnd">
    <span class="form-item-title" v-if="title !== undefined && title !== ''">
      {{ title }}
    </span>
    <slot name="input">
      <label class="form-select-list-c">
        <span v-if="label !== undefined && label !== ''">
          {{ label }}
          <i v-if="required" class="form-item-alert">*</i>
        </span>
        <div
          tabindex="0"
          :class="[
            'form-select-activator',
            invalidText !== undefined && invalidText !== '' ? 'invalid' : '',
            iclass || '',
            disabled ? 'disabled' : '',
            disabledClass || ''
          ]"
          v-if="!activated"
          @click="OpenList">
          <slot name="activator" :activatorData="selectedOption">
            {{ activatorText }}
          </slot>
          <i
            class="ico-c ico-close"
            v-if="
              clearable &&
              localValue !== '' &&
              localValue !== undefined &&
              localValue !== null
            "
            @click="ClearSelection($event)">
            <svg><use href="/src/assets/images/icons.svg#close"></use></svg>
          </i>
          <i class="ico-c">
            <svg><use href="/src/assets/images/icons.svg#arrow"></use></svg>
          </i>
        </div>
        <div class="form-select-input" v-if="activated">
          <form-input v-model="q" placeholder="Listede arayın" />
        </div>
        <teleport to="body" v-if="activated">
          <div class="form-select-list" :id="'selectlist' + rnd">
            <i
              class="ico-c ico-close select-close-selection"
              @click="CloseListFromSelection">
              <svg><use href="/src/assets/images/icons.svg#close"></use></svg>
            </i>
            <div class="form-select-list-item" v-if="selectList.length === 0">
              <span>Sonuç Bulunamadı</span>
            </div>
            <template v-else>
              <div class="form-select-list-item" v-if="showSearchPrompt">
                <span>
                  İlk {{ selectList.length }} kayıt gösteriliyor. Daha fazla sonuç için en az
                  {{ props.minSearchLength }} harf yazın.
                </span>
              </div>
              <div
                class="form-select-list-item"
                v-if="multiple"
                @click="MultipleSelectAll($event)">
                <input
                  class="select-box-check"
                  type="checkbox"
                  v-model="multipleAllSelected" />
                <span>{{ multipleText }}</span>
              </div>
              <template v-for="(item, i) in selectList">
                <div
                  v-if="localView(item)"
                  :class="['form-select-list-item', isSelected(item) ? 'selected' : '']"
                  @click="updateVal($event, item)">
                  <input
                    v-if="multiple"
                    class="select-box-check"
                    type="checkbox"
                    :value="listVal !== undefined ? item[listVal] : item"
                    v-model="localValue" />
                  <slot name="option" :optionData="item">
                    <span>{{ listText !== undefined ? item[listText] : item }}</span>
                  </slot>
                </div>
              </template>
            </template>
          </div>
        </teleport>
        <span
          class="form-item-alert"
          v-if="InvalidMessages.length > 0 && InvalidMessages !== ''">
          {{ InvalidMessages }}
        </span>
        <span
          class="form-item-description"
          v-if="description !== undefined && description !== ''">
          {{ description }}
        </span>
      </label>
      <div v-if="$slots.button" class="form-item-button"><slot name="button"></slot></div>
    </slot>
  </div>
</template>
<script setup lang="ts">
  import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
  import { useGlobalStore } from '@/stores/globalStore'
  const globalStore = useGlobalStore()

  export interface Props {
    listData: Record<string, any>[]
    listText?: string
    listVal?: string
    label?: string
    disabled?: boolean
    modelValue: string | number | number[] | string[] | null
    half?: boolean
    title?: string
    invalidText?: string
    description?: string
    placeholder?: string
    required?: boolean
    iclass?: string
    elclass?: string
    clearable?: boolean
    extraData?: any
    multiple?: boolean
    multipleText?: string
    view?: Function
    disabledClass?: string
    largeListThreshold?: number
    initialDisplayLimit?: number
    minSearchLength?: number
    searchDebounceMs?: number
  }

  const props = withDefaults(defineProps<Props>(), {
    listData: () => [],
    disabled: false,
    half: false,
    required: false,
    clearable: false,
    extraData: '',
    multiple: false,
    multipleText: 'Tümü',
    largeListThreshold: 100,
    initialDisplayLimit: 100,
    minSearchLength: 2,
    searchDebounceMs: 120
  })

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

  const activated = ref<Boolean>(false)
  const multipleAllSelected = ref<boolean>(false)
  const q = ref<string>('')
  const debouncedQuery = ref<string>('')
  const rnd = ref<number>(Number(Math.floor(Math.random() * 1000000000)))
  const localValue = ref<string | number | boolean | null | string[]>()
  const selectedOption = ref<Record<string, any>>({})
  let searchDebounceTimer: ReturnType<typeof setTimeout> | undefined

  if (props.multiple) {
    if (props.modelValue === null || props.modelValue === undefined) {
      localValue.value = [] as string[]
    } else {
      localValue.value = props.modelValue as string[]
    }
  } else {
    localValue.value = props.modelValue as string
  }

  const localView = (data: Record<string, any>) => {
    if (props.view !== undefined) {
      if (props.extraData !== undefined) return props.view(data, props.extraData)
      else return props.view(data, props)
    } else {
      return true
    }
  }

  const localExtra = ref<any>(props.extraData)

  const InvalidMessageText = reactive<Record<string, any>>({})

  const InvalidMessages = computed<string>(() => {
    let text = ''
    Object.keys(InvalidMessageText).forEach((k: string, i: number) => {
      text += InvalidMessageText[k] + ', '
    })
    return text
  })
  const activatorText = computed<any>(() => {
    if (
      localValue.value === '' ||
      localValue.value === null ||
      localValue.value === undefined
    ) {
      return props.placeholder || 'Lütfen Seçim Yapınız'
    } else {
      if (props.multiple) {
        if (multipleAllSelected.value) {
          return props.multipleText
        } else {
          let textm = props.listData.filter((v: Record<string, any>) => {
            let val = props.listVal !== undefined ? v[props.listVal] : v
            return (localValue.value as string[]).includes(val)
          })
          if (textm !== undefined) {
            if (props.listVal !== undefined) {
              let listText = ''
              textm.forEach((itm: Record<string, any>) => {
                listText += itm[props.listText as string] + ', '
              })
              return listText.slice(0, -2)
            } else {
              return textm.toString()
            }
          } else {
            return 'Lütfen Seçim Yapınız'
          }
        }
      } else {
        let text = props.listData.filter((v: Record<string, any>) => {
          let val = props.listVal !== undefined ? v[props.listVal] : v
          return String(localValue.value) === String(val)
        })[0]
        return text !== undefined
          ? props.listText !== undefined
            ? text[props.listText]
            : text
          : 'Lütfen Seçim Yapınız'
      }
    }
  })

  const isLargeList = computed<boolean>(() => {
    return props.listData.length > props.largeListThreshold
  })

  const showSearchPrompt = computed<boolean>(() => {
    return isLargeList.value && debouncedQuery.value.length < props.minSearchLength
  })

  const selectList = computed<Record<string, any>[]>(() => {
    if (debouncedQuery.value.length >= props.minSearchLength) {
      return props.listData.filter((v: Record<string, any>) => {
        let val = props.listText !== undefined ? v[props.listText] : v
        return globalStore
          .trToUpper(String(val))
          .includes(globalStore.trToUpper(debouncedQuery.value))
      })
    } else if (isLargeList.value) {
      return props.listData.slice(0, props.initialDisplayLimit)
    } else {
      return props.listData
    }
  })

  const isSelected = (d: Record<string, any>): boolean => {
    if (localValue.value !== null && localValue.value !== undefined) {
      let val = props.listVal !== undefined ? d[props.listVal] : d
      return props.multiple
        ? (localValue.value as string[]).includes(val)
        : val === localValue.value
    } else {
      return false
    }
  }

  const MultipleSelectAll = (e: Event) => {
    if (props.multiple) {
      if (multipleAllSelected.value) {
        localValue.value = []
        multipleAllSelected.value = false
      } else {
        props.listData.forEach((data: any, i: number) => {
          if (props.listVal !== undefined)
            (localValue.value as string[]).push(data[props.listVal])
          else (localValue.value as string[]).push(data)
        })
        multipleAllSelected.value = true
      }
      let item: Record<string, any> = {}
      emit('update:modelValue', localValue.value as string[])
      emit('change', e, localValue.value, item, localExtra.value)
    }
  }
  const ClearSelection = (e: Event) => {
    e.stopPropagation()
    if (typeof localValue.value === 'number') localValue.value = null
    if (typeof localValue.value === 'string') localValue.value = ''
    if (typeof localValue.value === 'boolean') localValue.value = false

    let item: Record<string, any> = {}
    selectedOption.value = {}

    emit('update:modelValue', localValue.value)
    emit('change', e, localValue.value, item, localExtra.value)
    emit('clear', e, localValue.value)
  }

  const CloseListFromSelection = () => {
    activated.value = false
    document.removeEventListener('click', closeList)
  }

  const closeList = (e: Event) => {
    let p = e.composedPath && e.composedPath()
    var open = false
    if (p) {
      p.pop()
      p.forEach((pv) => {
        if (
          (pv as HTMLElement).id !== '' &&
          (pv as HTMLElement).id !== undefined &&
          (pv as HTMLElement).id.includes(String(rnd.value))
        )
          open = true
      })
    }
    if (!open) {
      activated.value = false
      document.removeEventListener('click', closeList)
    }
  }

  const OpenList = (e: Event) => {
    if (props.disabled) return
    e.stopPropagation()
    q.value = ''
    debouncedQuery.value = ''
    activated.value = true

    setTimeout(() => {
      let actEl = document.querySelector('#formselect' + rnd.value) as HTMLElement
      let el = document.querySelector('#selectlist' + rnd.value) as HTMLElement
      let rcAct = actEl.getBoundingClientRect() as DOMRect

      el.style.width = rcAct.width - 8 + 'px'
      el.style.left = rcAct.left + 4 + 'px'
      if (window.innerHeight - rcAct.bottom < 100) {
        el.style.bottom = window.innerHeight - rcAct.top - 24 + 'px'
        el.style.maxHeight = rcAct.top - 64 + 'px'
      } else {
        el.style.top = rcAct.bottom + 'px'
        el.style.maxHeight = window.innerHeight - rcAct.bottom - 16 + 'px'
      }
    }, 5)

    setTimeout(() => {
      document.addEventListener('click', closeList)
    }, 10)
  }

  const updateVal = (e: Event, item: Record<string, any>) => {
    e.stopPropagation()
    let val = props.listVal !== undefined ? item[props.listVal] : item
    if (props.multiple) {
      if ((localValue.value as string[]).indexOf(val) > -1)
        (localValue.value as string[]).splice(
          (localValue.value as string[]).indexOf(val),
          1
        )
      else (localValue.value as string[]).push(val)

      multipleAllSelected.value =
        (localValue.value as string[]).length === props.listData.length
    } else {
      localValue.value = val
      activated.value = false
      document.removeEventListener('click', closeList)
    }
    selectedOption.value = item
    emit('update:modelValue', localValue.value)
    emit('change', e, localValue.value, item, localExtra.value)
  }

  const SetSelectedOption = () => {
    selectedOption.value = props.listData.filter((v: Record<string, any>) => {
      return String(v[props.listVal as string]) === String(props.modelValue)
    })[0]
  }

  onMounted(() => {
    if (props.multiple) {
      multipleAllSelected.value =
        (localValue.value as string[]).length === props.listData.length
    }
    SetSelectedOption()
  })

  onUnmounted(() => {
    if (searchDebounceTimer !== undefined) clearTimeout(searchDebounceTimer)
    document.removeEventListener('click', closeList)
  })

  watch(q, (value: string) => {
    if (searchDebounceTimer !== undefined) clearTimeout(searchDebounceTimer)
    searchDebounceTimer = setTimeout(() => {
      debouncedQuery.value = value.trim()
    }, props.searchDebounceMs)
  })

  watch(
    () => props.invalidText,
    () => {
      if (props.invalidText !== undefined && props.invalidText !== '') {
        Object.assign(InvalidMessageText, {})
        InvalidMessageText.invalid = props.invalidText
      } else {
        delete InvalidMessageText.invalid
      }
    }
  )
  watch(
    () => props.listData,
    () => {
      if (props.modelValue !== undefined) {
        if (props.listData !== undefined) {
          SetSelectedOption()
        }
      }
    }
  )
</script>
