import { makeComponentProps } from '@/composables/component'
import { computed, ref, Ref } from 'vue'
import { useEventListener } from '@vueuse/core'
import '@/components/UDropdown/scrollbar.scss'

import DropdownVariant from '@/types/dropdownVariants'
import { Sizes } from '@/composables/size'

import { UIcon } from '@/components/UIcon/UIcon'
import { UAvatar } from '@/components/UAvatar/UAvatar'
import { UInput } from '@/components/UInput/UInput'
import { UTag } from '@/components/UTag/UTag'

import { genericComponent, propsFactory, useRender } from '@/utils'

export const makeUDropdownProps = propsFactory(
  {
    modelValue: Object,
    label: String,
    placeholder: String,
    hint: String,
    appendIcon: {
      type: String,
      default: 'chevron-down',
    },
    prependIcon: String,
    avatarIcon: String,
    variant: {
      type: String,
      default: DropdownVariant.default,
    },
    type: String,
    size: {
      type: String,
      default: 'md',
    },
    emptyMessage: String,
    disabled: {
      type: Boolean,
      default: false,
    },
    name: String,
    error: Boolean,
    errorMessages: Array as () => string[],
    dropDownItems: {
      type: Array<DropdownItem>,
      required: true,
    },

    ...makeComponentProps(),
  },
  'UDropdown'
)

export type UDropdownSlots = {
  default: never
  header: never
}

interface DropdownItem {
  id: string
  image: string
  text: string
  supportingText: string
}

export const UDropdown = genericComponent<UDropdownSlots>()({
  name: 'UDropdown',

  props: makeUDropdownProps(),

  emits: {
    submit: (e: string) => true,
    blur: () => true,
    'keydown:escape': () => true,
    'update:modelValue': (value: object) => true,
  },

  setup(props, { emit, slots }) {
    const isActive = ref(false)
    const selectedItem = ref()
    const selectedItems: Ref<number[]> = ref([])
    const isHover = ref(false)
    const inputValue = ref('')
    const dropdownRef = ref<HTMLElement | null>(null)
    const searchInputRef = ref<HTMLElement | null>(null)
    const tagsInputRef = ref<HTMLElement | null>(null)
    const tagsInputComponentRef = ref()
    const tagsInputContentRef = ref<HTMLElement | null>(null)
    const dropdownContentRef = ref<HTMLElement | null>(null)
    const inputFieldRef = ref<HTMLElement | null>(null)

    const toggleActive = () => {
      if (!props.disabled) isActive.value = !isActive.value
    }

    const getDropDownItems = (): DropdownItem[] => {
      const dropdownItems: DropdownItem[] = props.dropDownItems

      if (Array.isArray(dropdownItems)) {
        const updatedItems = dropdownItems.map((item) => {
          if (item.image || item.text || item.supportingText) {
            return item
          } else {
            return {
              id: item.id || '',
              image: item.image || '',
              text: item.text || '',
              supportingText: item.supportingText || '',
            }
          }
        })

        const filteredItems = updatedItems.filter((item) => {
          return (
            item.image !== '' || item.text !== '' || item.supportingText !== ''
          )
        })

        return filteredItems
      } else {
        return []
      }
    }

    const isEnabled = computed(
      (): boolean => !!props.dropDownItems.length && !props.disabled
    )

    if (props.type === 'header' && isEnabled.value) {
      isActive.value = true
    }

    const inputHandler = () => {
      if (isEnabled.value) {
        isActive.value = true
      }
    }

    const inputClickHandler = () => {
      if (isEnabled.value) {
        if (isActive.value !== true) isActive.value = true
      }
    }

    const removeTag = (tagIndex: number) => {
      const dropDownItems = getDropDownItems()
      if (isEnabled.value) {
        selectedItems.value.splice(tagIndex, 1)
        emit(
          'update:modelValue',
          selectedItems.value.map((index: number) => dropDownItems[index])
        )
      }
    }

    const filteredDropDownItems = computed(() => {
      const dropDownItems = getDropDownItems()
      if (
        props.variant !== DropdownVariant.search &&
        props.variant !== DropdownVariant.tags
      ) {
        return dropDownItems
      } else {
        const inputValueLower =
          inputValue.value && inputValue.value.toLowerCase()
        const filteredItems = dropDownItems.filter(
          (item) =>
            item.text?.toLowerCase().startsWith(inputValueLower) ||
            item.supportingText?.toLowerCase().includes(inputValueLower)
        )
        return filteredItems.length > 0 ? filteredItems : null
      }
    })

    const selectItem = (index: number) => {
      const dropDownItems = getDropDownItems()
      if (!isEnabled.value) {
        return false
      }

      if (props.variant === DropdownVariant.tags) {
        const selectedItemIndex = dropDownItems.indexOf(
          filteredDropDownItems.value?.[index] as DropdownItem
        )

        const selectedItemPosition =
          selectedItems.value.indexOf(selectedItemIndex)
        if (selectedItemPosition === -1) {
          selectedItems.value.push(selectedItemIndex)
        } else {
          selectedItems.value.splice(selectedItemPosition, 1)
        }

        emit(
          'update:modelValue',
          selectedItems.value.map((index) => dropDownItems[index])
        )

        tagsInputComponentRef?.value?.target?.focus()

        return true
      }

      if (
        selectedItem.value ===
        dropDownItems.indexOf(
          filteredDropDownItems.value?.[index] as DropdownItem
        )
      ) {
        selectedItem.value = undefined
        emit('update:modelValue', selectedItem.value)
      } else {
        const filteredItems = filteredDropDownItems.value
        if (
          filteredItems !== null &&
          index >= 0 &&
          index < filteredItems.length
        ) {
          const selectedItemIndex = dropDownItems.indexOf(
            filteredItems[index] as DropdownItem
          )
          selectedItem.value = selectedItemIndex
          inputValue.value = ''
          emit('update:modelValue', dropDownItems[selectedItemIndex])
        }
      }
    }

    const handleKeyDown = (event: KeyboardEvent) => {
      const escapeKey = 'Escape'
      if (
        isActive.value &&
        isEnabled.value &&
        event.key === 'Backspace' &&
        props.variant !== DropdownVariant.tags &&
        props.type !== 'header'
      ) {
        selectedItem.value = undefined
        emit('update:modelValue', selectedItem.value)
      }
      if (isEnabled.value && event.key === escapeKey) {
        if (isActive.value) {
          toggleActive()
        }
        emit('keydown:escape')
      }
    }

    const handleClickOutside = (event: MouseEvent) => {
      if (
        (isActive.value &&
          (!dropdownRef.value ||
            (!dropdownRef.value.contains(event.target as Node) &&
              !dropdownContentRef.value?.contains(event.target as Node))) &&
          !searchInputRef.value?.contains(event.target as Node) &&
          !tagsInputRef.value?.contains(event.target as Node)) ||
        tagsInputContentRef.value?.contains(event.target as Node)
      ) {
        isActive.value = false
        emit('blur')
      }
      if (dropdownContentRef.value?.contains(event.target as Node)) {
        isActive.value = true
      }
    }

    useEventListener('click', handleClickOutside)
    useEventListener('keydown', handleKeyDown)

    const classes = computed(() => ({
      'field box-border flex items-center gap-2 font-regular rounded-md border px-3.5':
        true,
      ...(props.size == 'md' && {
        'max-h-[44px]': true,
      }),
      ...(props.size == 'sm' && {
        'max-h-[40px]': true,
      }),
      ...(isActive.value === false &&
        props.error === false &&
        isEnabled.value === true && {
          'border-gray-300': true,
        }),
      ...(isActive.value === true &&
        props.error === false &&
        isEnabled.value === true && {
          'border-primary-300 shadow-xs-btn shadow-primary-50': true,
        }),
      ...(isActive.value === true &&
        props.error === true &&
        isEnabled.value === true && {
          'shadow-md shadow-error-50': true,
        }),
      ...(props.error === true &&
        isEnabled.value === true && {
          'border-error-300': true,
        }),
      ...(isEnabled.value === false && {
        'border-gray-300 bg-gray-50 cursor-not-allowed': true,
      }),
    }))

    const inputClasses = computed(() => ({
      // eslint-disable-next-line max-len
      'box-border flex-grow select-none bg-transparent font-regular text-gray-900 text-text-md py-2.5 overflow-hidden':
        true,
      ...(isEnabled.value === false && {
        'cursor-not-allowed': true,
      }),
    }))

    const placeholderClasses = computed(() => ({
      'placeholder font-regular text-gray-500 text-text-md': true,
    }))

    const textClasses = computed(() => ({
      // eslint-disable-next-line max-len
      'font-medium text-gray-900 text-text-md whitespace-nowrap text-ellipsis overflow-hidden max-w-[70%]':
        true,
    }))

    const supportingTextClasses = computed(() => ({
      // eslint-disable-next-line max-len
      'font-regular text-gray-600 whitespace-nowrap text-ellipsis overflow-hidden max-w-[30%]':
        true,
      ...(props.size == 'md' && {
        'text-text-md': true,
      }),
      ...(props.size == 'sm' && {
        'text-text-sm': true,
      }),
    }))

    const inputValueClasses = computed(() => ({
      'flex flex-row gap-2 items-center': true,
    }))

    const dropdownClasses = computed(() => ({
      'dropdownContent absolute right-0 flex flex-col gap-1 p-1.5 pr-0 w-full':
        true,
      'overflow-y-scroll bg-white rounded-md border border-gray-300 max-h-[320px]':
        true,
      ...(props.label &&
        props.size == 'md' && {
          'top-[75px]': true,
        }),
      ...(props.label &&
        props.size == 'sm' && {
          'top-[71px]': true,
        }),
      ...(!props.label &&
        props.size == 'md' && {
          'top-[49px]': true,
        }),
      ...(!props.label &&
        props.size == 'sm' && {
          'top-[44px]': true,
        }),
      ...(isEnabled.value === false && {
        'cursor-not-allowed': true,
      }),
    }))

    const dropdownItemClasses = computed(() => {
      let classes = `dropdownItem flex gap-2 px-2 py-2.5 cursor-pointer rounded-md hover:bg-gray-50 
        items-center select-none`

      if (!isEnabled.value) {
        classes += ' cursor-not-allowed'
      }

      return classes
    })

    const fieldClasses = computed(() => ({
      'box-border relative flex flex-col gap-1.5 w-full': true,
    }))

    const labelClasses = computed(() => ({
      'label font-medium text-gray-700 text-text-sm': true,
    }))

    const hintClasses = computed(() => ({
      'hint font-regular text-gray-600 text-text-sm': true,
    }))

    const errorMessageClasses = computed(() => ({
      'error font-regular text-error-500 text-text-sm': true,
    }))

    const dotClasses = computed(() => ({
      'm-0.5 w-2 h-2 bg-success-500 rounded': true,
    }))

    const tagsInputContentClasses = computed(() => ({
      'flex gap-1.5 items-center h-44': true,
    }))

    useRender(() => (
      <div class={fieldClasses.value} ref={inputFieldRef}>
        {props.label ? (
          <div class={labelClasses.value}>{props.label}</div>
        ) : null}

        {(props.variant === 'search' && selectedItem.value !== undefined) ||
        (props.variant !== 'search' && props.variant !== 'tags') ? (
          <div class={classes.value} onClick={toggleActive} ref={dropdownRef}>
            {props.prependIcon &&
            props.variant !== DropdownVariant.avatarLeading &&
            props.variant !== DropdownVariant.dotLeading ? (
              <div>
                <UIcon
                  color="gray-500"
                  size="20"
                  icon={`$${props.prependIcon}`}
                ></UIcon>
              </div>
            ) : null}
            {props.variant == DropdownVariant.avatarLeading &&
            selectedItem.value == undefined ? (
              <div>
                <UIcon
                  color="gray-500"
                  size="20"
                  icon={`$${props.prependIcon}`}
                ></UIcon>
              </div>
            ) : null}
            {props.variant === DropdownVariant.avatarLeading &&
            selectedItem.value !== null &&
            selectedItem.value !== undefined ? (
              <UAvatar
                size={Sizes.xs}
                imagePath={getDropDownItems()[selectedItem.value]?.image}
                avatarIcon={props.avatarIcon}
              ></UAvatar>
            ) : null}
            {props.variant === DropdownVariant.dotLeading ? (
              <div class={dotClasses.value}></div>
            ) : null}
            <div class={inputClasses.value}>
              {selectedItem.value == null && selectedItem.value == undefined ? (
                <div class={placeholderClasses.value}>{props.placeholder}</div>
              ) : null}
              {selectedItem.value !== null &&
              selectedItem.value !== undefined ? (
                <div class={inputValueClasses.value}>
                  <div class={textClasses.value}>
                    {getDropDownItems()[selectedItem.value]?.text}
                  </div>
                  <div class={supportingTextClasses}>
                    {getDropDownItems()[selectedItem.value]?.supportingText}
                  </div>
                </div>
              ) : null}
            </div>
            <UIcon
              color="gray-500"
              size="20"
              icon={`$${props.appendIcon}`}
            ></UIcon>
          </div>
        ) : null}
        {props.variant == DropdownVariant.search &&
        selectedItem.value == undefined ? (
          <div ref={searchInputRef}>
            <UInput
              v-model={inputValue.value}
              size={props.size}
              autocomplete={false}
              prependIcon={props.prependIcon}
              placeholder={props.placeholder}
              enabled={isEnabled.value}
              onClick={inputClickHandler}
              onInput={inputHandler}
            ></UInput>
          </div>
        ) : null}
        {props.variant == DropdownVariant.tags ? (
          <div>
            <div ref={tagsInputRef}>
              <UInput
                v-model={inputValue.value}
                ref={tagsInputComponentRef}
                size={Sizes.lg}
                autocomplete={false}
                placeholder={props.placeholder}
                name="tagsInput"
                enabled={isEnabled.value}
                onClick={inputClickHandler}
                onInput={inputHandler}
              >
                <div
                  ref={tagsInputContentRef}
                  class={tagsInputContentClasses.value}
                >
                  <UIcon
                    color="gray-500"
                    size="20"
                    icon={`$${props.prependIcon}`}
                  ></UIcon>
                  {selectedItems.value.map(
                    (itemIndex: number, tagIndex: number) => (
                      <UTag
                        onDelete={() => removeTag(tagIndex)}
                        key={tagIndex}
                        class="whitespace-nowrap"
                        avatarImagePath={getDropDownItems()[itemIndex]?.image}
                        closed={isEnabled.value}
                        size={Sizes.sm}
                      >
                        {getDropDownItems()[itemIndex]?.supportingText}
                      </UTag>
                    )
                  )}
                </div>
              </UInput>
            </div>
          </div>
        ) : null}

        {(!props.error && props.hint && !isActive.value) ||
        (!filteredDropDownItems.value && props.hint) ? (
          <div class={hintClasses.value}>{props.hint}</div>
        ) : null}

        {isActive.value && filteredDropDownItems.value ? (
          <div class={dropdownClasses.value} ref={dropdownContentRef}>
            {filteredDropDownItems.value.map((item, index) => (
              <div
                key={index}
                class={`${dropdownItemClasses.value}${
                  (props.variant !== DropdownVariant.tags &&
                    index === selectedItem.value) ||
                  (props.variant === DropdownVariant.tags &&
                    filteredDropDownItems.value &&
                    selectedItems.value.includes(
                      getDropDownItems().indexOf(
                        filteredDropDownItems.value[index]
                      )
                    ))
                    ? ' bg-gray-50'
                    : ''
                }`}
                onClick={() => selectItem(index)}
              >
                {props.prependIcon &&
                  props.variant !== DropdownVariant.avatarLeading &&
                  props.variant !== DropdownVariant.dotLeading &&
                  props.variant !== DropdownVariant.search && (
                    <UIcon
                      color="gray-500"
                      size="20"
                      icon={`$${props.prependIcon}`}
                    ></UIcon>
                  )}
                {props.variant === DropdownVariant.avatarLeading && (
                  <UAvatar
                    size={Sizes.xs}
                    imagePath={item?.image}
                    avatarIcon={props.avatarIcon}
                  ></UAvatar>
                )}
                {props.variant === DropdownVariant.dotLeading && (
                  <div class={dotClasses}></div>
                )}
                <div class={textClasses.value}>{item.text}</div>
                <div class={supportingTextClasses.value}>
                  {item.supportingText}
                </div>
                <div class="ml-auto">
                  {props.variant !== DropdownVariant.tags &&
                    index === selectedItem.value && (
                      <UIcon
                        color="primary-600"
                        size="20"
                        icon="$check"
                      ></UIcon>
                    )}
                  {props.variant === DropdownVariant.tags &&
                    filteredDropDownItems.value &&
                    selectedItems.value.includes(
                      getDropDownItems().indexOf(
                        filteredDropDownItems.value[index]
                      )
                    ) && (
                      <UIcon
                        color="primary-600"
                        size="20"
                        icon="$check"
                      ></UIcon>
                    )}
                </div>
              </div>
            ))}
          </div>
        ) : null}
        {!filteredDropDownItems.value &&
        isActive.value &&
        props.emptyMessage ? (
          <div class={dropdownClasses.value}>
            <div class="text-text-sm font-medium text-gray-400 py-2 px-[14px] select-none">
              {props.emptyMessage}
            </div>
          </div>
        ) : null}
        {props.error && props.errorMessages ? (
          <div>
            {props.errorMessages.map((errorMessage) => (
              <div key={errorMessage} class={errorMessageClasses.value}>
                {errorMessage}
              </div>
            ))}
          </div>
        ) : null}
      </div>
    ))

    return {
      isActive,
      isEnabled,
    }
  },
})

export type UDropdown = InstanceType<typeof UDropdown>
