import "../../../../src/components/form/sass/form.sass";
import "../../../../src/components/dropdown/dropdown.sass";
import "../../../../src/components/form/autocomplete/autocomplete.sass";
import { StaticUseInputProps } from '../../../composables/input/useInput';
import { getUseModelPropsDefinition } from '../../../composables/model/useModel';
import { useProxy } from '../../../composables/proxy';
import { getEqPropsDefinition } from '../../../composables/shared';
import { useThemePropsDefinition } from '../../../composables/theme';
import { constEmptyArray, extractProp, isFunction, isHTMLElement, isObject, isString, toggleListItem } from '../../../utils/helpers';
import { DropdownThemeMap } from '../../dropdown';
import BDropdown from '../../dropdown/BDropdown';
import { isArrowDownEvent, isArrowUpEvent, isEnterEvent, isEscEvent, isTabEvent } from '../../../utils/eventHelpers';
import { constant, constVoid } from 'fp-ts/lib/function';
import BDropdownDivider from '../../dropdown/BDropdownDivider';
import BDropdownItem from '../../dropdown/BDropdownItem';
import { head, isEmpty, lookup } from 'fp-ts/lib/Array';
import { alt, chain, fold, fromNullable, isSome, map, none, some, toUndefined } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/lib/pipeable';
import { defineComponent, shallowRef, computed, onBeforeUpdate, nextTick, toRef, h } from 'vue';
import { BInput } from '../input';

function getActiveDescendentId(selectedItems, itemId) {
  return pipe(selectedItems, head, chain(item => {
    if (isString(item) && isFunction(itemId)) {
      const id = extractProp(itemId, item);
      return isString(id) ? some(id) : none;
    }

    if (isString(item)) {
      return some(item);
    }

    if (isString(itemId) && isObject(item) && Object.hasOwnProperty.call(item, itemId)) {
      const id = item[itemId];
      return isString(id) ? some(id) : none;
    }

    const id = extractProp(itemId, item);
    return isString(id) ? some(id) : none;
  }), toUndefined);
}

function getAutocompleteItems(items, selectedItems, itemId, itemText, eq, hoveredItem) {
  return items.map((item, index) => {
    const id = extractProp(itemId, item);
    const nid = isString(id) ? id : String(id);
    const text = extractProp(itemText, item);
    return {
      id: nid,
      isSelected: selectedItems.some(i => eq.equals(i, item)),
      isHovered: isSome(hoveredItem) ? hoveredItem.value.id === nid : false,
      text: isString(text) ? text : String(text),
      value: item,
      index
    };
  });
}

function getSetSelected(props, closeDropdown, inputModel, selectedItemsModel) {
  const toggle = toggleListItem(props.eq);
  return item => {
    const text = extractProp(props.itemText, item.value);
    inputModel.value = props.clearOnSelect ? '' : isString(text) ? text : String(text);
    selectedItemsModel.value = toggle(item.value, selectedItemsModel.value || []);

    if (props.closeOnSelect) {
      closeDropdown();
    }
  };
}

function getSetHovered(hoveredItem, templateItems) {
  return item => {
    const newItem = fromNullable(item);

    if (isSome(newItem)) {
      hoveredItem.value = newItem;
      pipe(newItem, map(item => item.index), chain(index => lookup(index, templateItems.value)), fold(constant(constVoid), li => () => li.focus && li.focus()))();
    }
  };
}

function getOnKeydown(autocompleteItems, hoveredItem, closeDropdown, setSelected, setHovered) {
  function onArrowPress(isUp) {
    pipe(hoveredItem.value, map(item => item.index), alt(() => some(0)), chain(index => lookup(isUp ? Math.max(index - 1, 0) : Math.min(index + 1, autocompleteItems.value.length - 1), autocompleteItems.value)), fold(constant(constVoid), newItem => () => setHovered(newItem)))();
  }

  return function onKeydown(event) {
    if (isEnterEvent(event)) {
      event.preventDefault();

      if (isSome(hoveredItem.value)) {
        setSelected(hoveredItem.value.value);
      }
    } else if (isTabEvent(event)) {
      event.preventDefault();

      if (isSome(hoveredItem.value)) {
        setSelected(hoveredItem.value.value);
      } else {
        nextTick(closeDropdown);
      }
    } else if (isArrowUpEvent(event)) {
      event.preventDefault();
      onArrowPress(true);
    } else if (isArrowDownEvent(event)) {
      event.preventDefault();
      onArrowPress(false);
    } else if (isEscEvent(event)) {
      event.preventDefault();
      nextTick(closeDropdown);
    }
  };
}

function getGenerateItem(itemsRef, length, onKeydown, setSelected, setHovered, slots) {
  return function generateItem(item, index) {
    return h(BDropdownItem, {
      key: item.id,
      ref: el => {
        if (isHTMLElement(el)) {
          itemsRef.value[index] = el;
        }
      },
      id: item.id,
      isActive: item.isSelected,
      tabindex: item.isSelected ? -1 : 0,
      'aria-selected': item.isSelected,
      'aria-label': `Option ${index + 1} of ${length.value}`,
      class: {
        'is-hovered': item.isHovered
      },
      onClick: () => setSelected(item),
      onMouseenter: () => setHovered(item),
      onKeydown
    }, () => slots.default ? slots.default({
      option: item,
      index
    }) : item.text);
  };
}

function generateHeaderItem(slots) {
  return h('li', {
    class: 'dropdown-item',
    tabindex: -1
  }, slots.header && slots.header());
}

function generateFooterItem(slots) {
  return h('li', {
    class: 'dropdown-item',
    tabindex: -1
  }, slots.footer && slots.footer());
}

function generateLoadingItem(slots) {
  return h('li', {
    tabindex: -1
  }, [h(BDropdownItem, {
    tag: 'div'
  }, () => slots.loading ? slots.loading() : 'Loading results...')]);
}

function generateEmptyItem(modelValue, slots) {
  return h(BDropdownItem, {
    class: 'is-disabled'
  }, () => slots.empty ? slots.empty({
    searchValue: modelValue
  }) : modelValue ? `No results` : `No results for ${modelValue}`);
}

function defineAutocomplete() {
  return defineComponent({
    name: 'b-autocomplete',
    props: { ...StaticUseInputProps,
      ...getEqPropsDefinition(),
      ...useThemePropsDefinition(DropdownThemeMap),
      ...getUseModelPropsDefinition(),
      ...getUseModelPropsDefinition('selectedItems', 'onUpdate:selectedItems'),
      selectedItems: {
        type: Array,
        required: true
      },
      items: {
        type: Array,
        default: constEmptyArray
      },
      itemFilter: {
        type: Function,
        required: false
      },
      itemId: {
        type: [String, Function],
        default: 'id'
      },
      itemText: {
        type: [String, Function],
        default: 'text'
      },
      closeOnSelect: {
        type: Boolean,
        default: true
      },
      clearOnSelect: {
        type: Boolean,
        default: true
      },
      openOnFocus: {
        type: Boolean,
        default: true
      },
      onSelected: {
        type: Function,
        required: false
      }
    },

    setup(props, {
      slots
    }) {
      const {
        value: searchValue
      } = useProxy(computed(() => props.modelValue ?? ''), toRef(props, 'onUpdate:modelValue'));
      const {
        value: selectedItems
      } = useProxy(toRef(props, 'selectedItems'), toRef(props, 'onUpdate:selectedItems'));
      const itemsRef = shallowRef([]);
      const filteredItems = computed(() => {
        if (props.itemFilter) {
          return props.items.filter(props.itemFilter(searchValue.value));
        } else {
          const sv = searchValue.value.toLowerCase();
          const extract = props.itemText;
          return props.items.filter(i => extractProp(extract, i).toLowerCase().includes(sv));
        }
      });
      onBeforeUpdate(() => {
        itemsRef.value = [];
      });
      const dropdown = shallowRef(null);

      function close() {
        dropdown.value && dropdown.value.toggle.setOff();
      }

      const hoveredItem = shallowRef(none);
      const activeDescendentId = computed(() => getActiveDescendentId(props.selectedItems, props.itemId));
      const autocompleteItems = computed(() => getAutocompleteItems(filteredItems.value, selectedItems.value, props.itemId, props.itemText, props.eq, hoveredItem.value));
      const numberOfItems = computed(() => autocompleteItems.value.length);
      const setSelected = getSetSelected(props, close, searchValue, selectedItems);
      const setHovered = getSetHovered(hoveredItem, itemsRef);
      const onKeydown = getOnKeydown(autocompleteItems, hoveredItem, close, setSelected, setHovered);
      const generateItem = getGenerateItem(itemsRef, numberOfItems, onKeydown, setSelected, setHovered, slots);
      return () => {
        return h(BDropdown, {
          ref: dropdown,
          isMobileModal: false,
          class: ['b-autocomplete', {
            'is-expanded': props.isExpanded
          }]
        }, {
          trigger: () => {
            return h(BInput, {
              modelValue: searchValue.value,
              type: 'text',
              size: props.size,
              isLoading: props.isLoading,
              isRounded: props.isRounded,
              icon: props.icon,
              maxlength: props.maxlength,
              autocomplete: 'off',
              placeholder: props.placeholder,
              role: 'searchbox',
              'aria-activedescendant': activeDescendentId.value,
              'onUpdate:modelValue': val => {
                searchValue.value = val;
              },
              // onFocus: () => {
              //   nextTick().then(() => {
              //     if (props.openOnFocus && toggle.isOff.value) {
              //       toggle.setOn();
              //     }
              //   });
              // },
              onBlur: props.onBlur,
              onKeydown
            });
          },
          default: () => {
            let nodes;

            if (props.isLoading) {
              nodes = [generateLoadingItem(slots)];
            } else {
              nodes = isEmpty(autocompleteItems.value) ? [generateEmptyItem(searchValue.value, slots)] : autocompleteItems.value.map(generateItem);

              if (slots.header) {
                nodes.unshift(generateHeaderItem(slots), h(BDropdownDivider));
              }

              if (slots.footer) {
                nodes.push(h(BDropdownDivider), generateFooterItem(slots));
              }
            }

            return nodes;
          }
        });
      };
    }

  });
}

export const BAutocomplete = defineAutocomplete();
//# sourceMappingURL=BAutocomplete.js.map