import debounce from 'lodash/debounce';
import { translate } from '../../../../../../../utils/i18n';
import GlCard from '../../../../../../base/card/card';
import GlDuoChatContextItemSelections from '../duo_chat_context_item_selections/duo_chat_context_item_selections';
import { contextItemsValidator, categoriesValidator, wrapIndex } from '../utils';
import GlDuoChatContextItemMenuCategoryItems from './duo_chat_context_item_menu_category_items';
import GlDuoChatContextItemMenuSearchItems from './duo_chat_context_item_menu_search_items';
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';

const SEARCH_DEBOUNCE_MS = 30;
var script = {
  name: 'GlDuoChatContextItemMenu',
  components: {
    GlCard,
    GlDuoChatContextItemMenuCategoryItems,
    GlDuoChatContextItemMenuSearchItems,
    GlDuoChatContextItemSelections
  },
  props: {
    /**
     * Whether the menu is open.
     */
    open: {
      type: Boolean,
      required: true
    },
    /**
     * Array of selected context items.
     */
    selections: {
      type: Array,
      required: true,
      validator: contextItemsValidator
    },
    /**
     * Whether the menu is in a loading state.
     */
    loading: {
      type: Boolean,
      required: true
    },
    /**
     * Error message to display, if any.
     */
    error: {
      type: [String, null],
      required: false,
      default: null
    },
    /**
     * Array of available categories for context items.
     */
    categories: {
      type: Array,
      required: true,
      validator: categoriesValidator
    },
    /**
     * Array of search results for context items.
     */
    results: {
      type: Array,
      required: true,
      validator: contextItemsValidator
    }
  },
  data() {
    return {
      selectedCategory: null,
      searchQuery: '',
      activeIndex: 0
    };
  },
  computed: {
    showCategorySelection() {
      return this.open && !this.selectedCategory;
    },
    allResultsAreDisabled() {
      return this.results.every(result => !result.metadata.enabled);
    }
  },
  watch: {
    open(isOpen) {
      if (!isOpen) {
        this.resetSelection();
      }
    },
    searchQuery(query) {
      this.debouncedSearch(query);
    },
    results(newResults) {
      const firstEnabledIndex = newResults.findIndex(result => result.metadata.enabled);
      this.activeIndex = firstEnabledIndex >= 0 ? firstEnabledIndex : 0;
    }
  },
  methods: {
    selectCategory(category) {
      this.searchQuery = '';
      this.selectedCategory = category;
      this.$emit('search', {
        category: category.value,
        query: ''
      });
    },
    debouncedSearch: debounce(function search(query) {
      /**
       * Emitted when a search should be performed.
       * @property {Object} filter
       * @property {string} filter.category - The value of the selected category
       * @property {string} filter.query - The search query
       */
      this.$emit('search', {
        category: this.selectedCategory.value,
        query
      });
    }, SEARCH_DEBOUNCE_MS),
    selectItem(item) {
      if (!item.metadata.enabled) {
        return;
      }

      /**
       * Emitted when a context item is selected.
       * @property {Object} item - The selected context item
       */
      this.$emit('select', this.results.find(result => result.id === item.id));

      /**
       * Emitted when the menu should be closed.
       */
      this.$emit('close');
      this.resetSelection();
    },
    removeItem(item) {
      /**
       * Emitted when a context item should be removed.
       * @property {Object} item - The context item to be removed
       */
      this.$emit('remove', item);
    },
    resetSelection() {
      this.selectedCategory = null;
      this.searchQuery = '';
      this.activeIndex = 0;
    },
    async scrollActiveItemIntoView() {
      await this.$nextTick();
      const activeItem = document.getElementById(`dropdown-item-${this.activeIndex}`);
      if (activeItem) {
        activeItem.scrollIntoView({
          block: 'nearest',
          inline: 'start'
        });
      }
    },
    handleKeyUp(e) {
      switch (e.key) {
        case 'ArrowDown':
        case 'ArrowUp':
          e.preventDefault();
          this.moveActiveIndex(e.key === 'ArrowDown' ? 1 : -1);
          this.scrollActiveItemIntoView();
          break;
        case 'Enter':
          e.preventDefault();
          if (this.showCategorySelection) {
            this.selectCategory(this.categories[this.activeIndex]);
            return;
          }
          if (!this.results.length) {
            return;
          }
          this.selectItem(this.results[this.activeIndex]);
          break;
        case 'Escape':
          e.preventDefault();
          if (this.showCategorySelection) {
            this.$emit('close');
            return;
          }
          this.selectedCategory = null;

          /**
           * Emitted when the parent GlDuoChat component should refocus on the main prompt input
           */
          this.$emit('focus-prompt');
          break;
      }
    },
    moveActiveIndex(step) {
      if (this.showCategorySelection) {
        // Categories cannot be disabled, so just loop to the next/prev one
        this.activeIndex = wrapIndex(this.activeIndex, step, this.categories.length);
        return;
      }

      // Return early if there are no results or all results are disabled
      if (!this.results.length || this.allResultsAreDisabled) {
        return;
      }

      // contextItems CAN be disabled, so loop to next/prev but ensure we don't land on a disabled one
      let newIndex = this.activeIndex;
      do {
        newIndex = wrapIndex(newIndex, step, this.results.length);
        if (newIndex === this.activeIndex) {
          // If we've looped through all items and found no enabled ones, keep the current index
          return;
        }
      } while (!this.results[newIndex].metadata.enabled);
      this.activeIndex = newIndex;
    },
    onGetContextItemContent(contextItem) {
      /**
       * Emit get-context-item-content event that tells clients to load the full file content for a selected context item.
       * The fully hydrated context item should be updated in the context item selections.
       * @param {*} event An event containing the context item to hydrate
       */
      this.$emit('get-context-item-content', {
        contextItem
      });
    }
  },
  i18n: {
    selectedContextItemsTitle: translate('GlDuoChatContextItemMenu.selectedContextItemsTitle', 'Included references')
  }
};

/* script */
const __vue_script__ = script;

/* template */
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.selections.length)?_c('gl-duo-chat-context-item-selections',{staticClass:"gl-mb-3",attrs:{"selections":_vm.selections,"categories":_vm.categories,"removable":true,"title":_vm.$options.i18n.selectedContextItemsTitle,"default-collapsed":false},on:{"remove":_vm.removeItem,"get-content":_vm.onGetContextItemContent}}):_vm._e(),_vm._v(" "),(_vm.open)?_c('gl-card',{staticClass:"slash-commands !gl-absolute gl-bottom-0 gl-w-full gl-pl-0 gl-shadow-md",attrs:{"body-class":"!gl-p-2","data-testid":"context-item-menu"}},[(_vm.showCategorySelection)?_c('gl-duo-chat-context-item-menu-category-items',{attrs:{"active-index":_vm.activeIndex,"categories":_vm.categories},on:{"select":_vm.selectCategory,"active-index-change":function($event){_vm.activeIndex = $event;}}}):_c('gl-duo-chat-context-item-menu-search-items',{attrs:{"active-index":_vm.activeIndex,"category":_vm.selectedCategory,"loading":_vm.loading,"error":_vm.error,"results":_vm.results},on:{"select":_vm.selectItem,"keyup":_vm.handleKeyUp,"active-index-change":function($event){_vm.activeIndex = $event;}},model:{value:(_vm.searchQuery),callback:function ($$v) {_vm.searchQuery=$$v;},expression:"searchQuery"}})],1):_vm._e()],1)};
var __vue_staticRenderFns__ = [];

  /* style */
  const __vue_inject_styles__ = undefined;
  /* scoped */
  const __vue_scope_id__ = undefined;
  /* module identifier */
  const __vue_module_identifier__ = undefined;
  /* functional template */
  const __vue_is_functional_template__ = false;
  /* style inject */
  
  /* style inject SSR */
  
  /* style inject shadow dom */
  

  
  const __vue_component__ = __vue_normalize__(
    { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
    __vue_inject_styles__,
    __vue_script__,
    __vue_scope_id__,
    __vue_is_functional_template__,
    __vue_module_identifier__,
    false,
    undefined,
    undefined,
    undefined
  );

export default __vue_component__;
