import {
  defineComponent, type Ref, type PropType, ref, computed,
} from 'vue'
import useClickOutside from '../../composables/use-click-outside'
import defineTemplate from './template'
import type { SelectSlotParams } from './types'

import './styles.scss'

export interface DefaultSelectExposed {
  isActive: boolean
}

/**
 * Пользовательский компонент для отображения селекта, соответствующий дизайну.
 * <p>
 *   <a href="https://www.figma.com/file/Q4Z4CnFx4i0qhD5fbgnPpf/%F0%9F%92%BB-%D0%94%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-ARK-SPACE?type=design&node-id=5294%3A72108&mode=design&t=Kgyiw2SSykhiVCLN-1" target="_blank">Дизайн</a>
 * </p>
 */
export const defineDefaultSelect = <T extends {id: string, name: string}> () => defineComponent({
  name: 'DefaultSelect',
  props: {
    /**
     * Опция копирования значения
     */
    isWithCopy: {
      type: Boolean,
      default: false,
    },
    /**
     * Вариант отображения селекта (filter, form)
     */
    appearance: {
      type: String as PropType<'filter'|'form'>,
      default: 'filter',
    },

    /**
       * Пользовательская установка значения **подсветки** \
       */
    customHighlightSearch: {
      type: Function as PropType<(value: string) => string>,
      default: undefined,
    },

    /**
     * Скрытие поиска
     */
    hideSearch: {
      type: Boolean,
      default: true,
    },
    /**
     * Значение селекта
     */
    modelValue: {
      type: Object as PropType<T|undefined>,
      default: undefined,
    },
    /**
     * Название селекта. Располагается над селектом
     */
    title: {
      type: String,
    },
    /**
     * Текстовая подсказка в окне селекта при значении undefined в modelValue
     */
    placeholder: {
      type: String,
    },
    /**
     * Описание селекта. Располагается под селектом
     */
    description: {
      type: String,
      default: '',
    },
    /**
     * Функция смены значения
     */
    onValueChange: {
      type: Function as PropType<(value: T|undefined) => void>,
      required: true,
    },
    /**
     * Массив объектов для выпадающего списка
     * Пример: const items = [ { id: '1', name: 'Вариант 1' }, { id: '2', name: 'Вариант 2' } ]
     */
    items: {
      type: Array as PropType<T[]>,
      required: true,
    },
    /**
     * Функция со слотом кастомных элементов списка
     */
    itemSlot: {
      type: Function as PropType<(item: T, params: SelectSlotParams<T>) => JSX.Element>,
    },
    /**
     * Вкл/Выкл возможность очищать выбранное значение
     */
    resetable: {
      type: Boolean,
      default: true,
    },
    /**
     * Размер селекта
     */
    size: {
      type: String as PropType<'M'|'S'>,
    },
    /**
     * Флаг, указывающий, отключен ли селект
     */
    disabled: {
      type: Boolean,
      default: false,
    },
    /**
     * Массив ошибок
     */
    errors: {
      type: [Array, String] as PropType<string[] | string>,
      default: () => [],
    },
    /**
     * Число отображаемых элементов в раскрытом списке
     */
    showItemsCount: {
      type: Number,
      default: 6,
    },
    /**
     * Отметить как обязательный
     */
    isRequired: {
      type: Boolean,
      default: false,
    },
    /**
     * Флаг, в значении true отображает id каждого элемента
     */
    showDescriptionId: {
      type: Boolean,
      default: false,
    },
  },

  setup(props, app) {
    const isOpened = ref(false)
    const search = ref('')
    const root = ref(null) as unknown as Ref<HTMLElement>
    const select = ref(null) as unknown as Ref<HTMLElement>
    const triggerDown = ref(null) as unknown as Ref<HTMLElement>
    const triggerUp = ref(null) as unknown as Ref<HTMLElement>

    const value = computed(() => props.modelValue as T)

    const items = computed(() => {
      let result = props.items

      if (search.value) {
        result = props.items.filter(({ name }) => name.toLowerCase().includes(search.value.toLowerCase()))
      }

      return result
    })

    useClickOutside(select, () => {
      isOpened.value = false
    })

    const clear = () => {
      if (props.onValueChange) props.onValueChange(undefined)
    }

    app.expose({ isOpened })

    const toggleOpened = () => {
      isOpened.value = !isOpened.value
    }

    const toggleItem = (item: T) => {
      if (value.value?.id !== item.id && props.onValueChange) {
        props.onValueChange(item)
      }

      toggleOpened()
    }

    const isSelected = (item: T) => value.value?.id === item.id

    const SelectTemplate = defineTemplate<T>()

    const templateProps = computed(() => ({
      clear,
      close: () => { isOpened.value = false },
      isWithCopy: props.isWithCopy,
      disabled: props.disabled,
      errors: props.errors,
      isLoadingNext: false,
      isLoadingPrev: false,
      isOpened: isOpened.value,
      isSelected,
      items: items.value,
      itemSlot: props.itemSlot,
      modelValue: value.value ? [value.value] : [],
      placeholder: props.placeholder,
      description: props.description,
      isRequired: props.isRequired,
      resetable: props.resetable,
      root,
      select,
      title: props.title,
      toggleItem,
      toggleOpened,
      topItems: [],
      triggerUp,
      triggerDown,
      tableIconType: 'arrow',
      isSingle: true,
      appearance: props.appearance,
      size: props.size,
      showItemsCount: props.showItemsCount,
      showDescriptionId: props.showDescriptionId,
      hideSearch: props.hideSearch,
      search: search.value,
      setSearch: (s: string) => { search.value = s },
      customHighlightSearch: props.customHighlightSearch?.(search.value),
    }))

    return (): JSX.Element => <SelectTemplate {...templateProps.value}/>
  },
})

export default defineDefaultSelect<{id: string, name: string}>()
