<template>
  <div>
    <v-autocomplete
      v-bind:value="value"
      v-on:input="selectionMade($event)"
      :readonly="readonly"
      :disabled="disabled"
      return-object
      :items="searchResults"
      :label="label"
      item-text="name"
      item-value="id"
      item-disabled="!active"
      :error-messages="errorMessages"
      :loading="fetching"
      :placeholder="$ay.t('Search')"
      :search-input.sync="searchEntry"
      :filter="customFilter"
      hide-no-data
      :clearable="!readonly && canClear"
      :no-filter="isTagFilter"
      :append-icon="errorIcon"
      @click:append="handleErrorClick"
      @mousedown="dropdown"
    >
      <template v-slot:prepend-item v-if="hasError()">
        <div class="pl-2">
          <span class="error--text"> {{ entryError }}</span>
        </div>
      </template>

      <template v-slot:prepend>
        <v-icon @click="handleEditClick">{{ editIcon() }}</v-icon>
      </template>
    </v-autocomplete>
  </div>
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*  Xeslint-disable    */
////////////////////////////////////////////////////////////////////////////////////////////////////////////

export default {
  created() {
    this.fetchValueIfNotPresent();
  },
  data() {
    return {
      searchResults: [],
      entryError: null,
      searchEntry: null,
      lastSelection: null,
      fetching: false,
      isTagFilter: false,
      errorIcon: null,
      initialized: false
    };
  },
  props: {
    value: {
      type: Number,
      default: null
    },
    errorMessages: { type: Array, default: null },
    readonly: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false },
    ayaType: {
      type: Number,
      default: 0
    },
    includeInactive: {
      type: Boolean,
      default: false
    },
    showEditIcon: {
      type: Boolean,
      default: false
    },
    allowNoSelection: {
      type: Boolean,
      default: true
    },
    canClear: { type: Boolean, default: true },
    label: { type: String, default: "" },
    variant: {
      type: String,
      default: null
    }
  },
  watch: {
    ayaType(val, oldVal) {
      if (val != oldVal && oldVal != null) {
        //change of type so clear out the list
        this.searchResults = [];
        this.searchEntry = null;
        this.lastSelection = null;
        this.initialized = false;
      }
    },
    value(val) {
      this.fetchValueIfNotPresent();
    },
    searchEntry(val, oldVal) {
      let vm = this;
      //clear any local entryError
      vm.clearErrors();
      //if the search entry is in the results list then it's a drop down selection not a typed search so bail
      for (let i = 0; i < vm.searchResults.length; i++) {
        if (vm.searchResults[i].name == val) {
          return;
        }
      }
      if (!val || vm.fetching || !vm.initialized) {
        if (!vm.initialized) {
          vm.$nextTick(() => {
            vm.initialized = true;
          });
        }
        return;
      }

      this.doSearch(val);
    },
    entryError(val) {
      if (this.hasError()) {
        this.errorIcon = "$ayiQuestionCircle";
      } else {
        this.errorIcon = null;
      }
    }
  },
  methods: {
    //Get full selection for use on forms where we need the
    //full selection including name, active, id
    getFullSelectionValue() {
      return this.lastSelection;
    },
    hasError: function() {
      return this.entryError != null;
    },
    clearErrors: function() {
      this.entryError = null;
    },
    handleErrorClick: function() {
      //open help nav for picklist
      window.$gz.eventBus.$emit("menu-click", {
        key: "app:help",
        data: "ay-start-form-select-list/#searching"
      });
    },
    editIcon: function() {
      if (!this.showEditIcon) {
        return null;
      }
      if (this.value != null) {
        return "$ayiEdit";
      }
      return "$ayiPlus";
    },
    handleEditClick: function() {
      let idToOpen = 0;
      if (this.lastSelection != null && this.lastSelection.id) {
        idToOpen = this.lastSelection.id;
      }
      window.$gz.eventBus.$emit("openobject", {
        type: this.ayaType,
        id: idToOpen
      });
    },
    selectionMade(e) {
      this.clearErrors();
      if (e == undefined) {
        //this will happen when clear clicked
        //simulate empty selection:
        e = window.$gz.form.getNoSelectionItem(true);
      }

      this.lastSelection = e;

      //this is required for the control to update and parent form to detect it
      this.$emit("input", e.id);
    },
    fetchValueIfNotPresent() {
      //is there a value that might require fetching?

      let vm = this;
      let val = vm.value;
      if (val == null) {
        return;
      }
      //check if it's in the list of items we have here
      for (let i = 0; i < vm.searchResults.length; i++) {
        if (vm.searchResults[i].id == val) {
          return;
        }
      }
      //is it the no selection item?
      if (val == null) {
        window.$gz.form.addNoSelectionItem(vm.searchResults, true);
      } else {
        //Not here, better get it
        let pickListParams = {
          ayaType: vm.ayaType,
          preselectedIds: [vm.value]
        };

        if (vm.variant != null) {
          pickListParams["listVariant"] = vm.variant;
          //variantSegment = `&variant=${vm.variant}`;
        }
        // let pickListParams =
        //   "?ayaType=" + vm.ayaType + "&preId=" + vm.value + variantSegment;

        vm.getList(pickListParams);
      }
    },
    replaceLastSelection() {
      let vm = this;
      //check if searchResults has last selection, if not then add it back in again
      if (vm.lastSelection == null) {
        //it might be initializing
        for (let i = 0; i < vm.searchResults.length; i++) {
          if (vm.searchResults[i].id == vm.value) {
            vm.lastSelection = vm.searchResults[i];
            return;
          }
        }
        return;
      }

      for (let i = 0; i < vm.searchResults.length; i++) {
        if (vm.searchResults[i].id == vm.lastSelection.id) {
          return; //already there
        }
      }
      //Not there so insert it
      vm.searchResults.push(vm.lastSelection);
    },
    dropdown(e) {
      let vm = this;
      //check if we have only the initial loaded item and no selection item
      if (vm.searchResults.length < 3) {
        //get the default list
        vm.getList();
      }
    },
    customFilter(item, queryText, itemText) {
      //NOTE: I wanted this to work with tags but all it does is highlight all of each row if tag query is present
      //I guess because it later on attempts to do the highlighting and can't find all the entered query
      //it's not clean so I'm just going to make it only highlight if it's a non tag query for now
      //and do no filtering (highlighting) at all if it's a tag query
      if (queryText.includes(" ") || queryText.startsWith("..")) {
        this.isTagFilter = true;
        return false;
      }
      if (this.$store.state.globalSettings.searchCaseSensitiveOnly == true) {
        return item.name.indexOf(queryText) > -1;
      } else {
        //need to do a case insensitive filter and hopefully it mirrors postgres at the backend
        return item.name.toLowerCase().indexOf(queryText.toLowerCase()) > -1;
      }

      //split out just the text search part
      // let searchText = queryText;
      //   if (queryText.includes(" ")) {
      //     //get the non tag part of query if possible
      //     //ignore bad condition of too many terms
      //     let searchTerms = queryText.split(" ");
      //     if (searchTerms[0].includes("..")) {
      //       searchText = searchTerms[1];
      //     } else {
      //       searchText = searchTerms[0];
      //     }
      //   }
    },
    getList: async function(pickListParams) {
      /*         
        public AyaType AyaType { get; set; }
        public string Query { get; set; }
        public bool Inactive { get; set; }
        public List<long> PreselectedIds { get; set; }
        public string ListVariant { get; set; }        
        //"{\"ayaType\":8,\"preselectedIds\":[1,3,5,7]}"
    }

          */
      let vm = this;
      if (vm.fetching) {
        return;
      }
      vm.fetching = true;
      //default params for when called on init
      if (!pickListParams) {
        pickListParams = {
          ayaType: vm.ayaType
        };

        if (vm.value != null) {
          pickListParams["preselectedIds"] = [vm.value];
        }

        if (vm.includeInactive) {
          pickListParams["inactive"] = true;
        }
        if (vm.variant != null) {
          pickListParams["listVariant"] = vm.variant;
        }
      }
      try {
        let res = await window.$gz.api.upsert("pick-list/list", pickListParams);

        //  let res = await window.$gz.api.get("pick-list/list" + urlParams);

        vm.fetching = false;
        //We never expect there to be no data here
        if (!res.hasOwnProperty("data")) {
          return Promise.reject(res);
        }
        vm.searchResults = res.data;
        if (vm.allowNoSelection) {
          window.$gz.form.addNoSelectionItem(vm.searchResults, true);
        }
        vm.replaceLastSelection();
      } catch (err) {
        window.$gz.errorHandler.handleFormError(err);
        vm.fetching = false;
      }
    },
    doSearch: debounce(function(searchFor) {
      //NOTE debounce with a watcher is a bit different, currently it has to be done exactly this way, nothing else will work properly
      //https://vuejs.org/v2/guide/migration.html#debounce-Param-Attribute-for-v-model-removed
      //-----------------

      let vm = this;
      let isATwoTermQuery = false;
      let queryTerms = [];
      //NOTE: empty query is valid; it means get the top 100 ordered by template order
      let emptyQuery = false;
      if (searchFor == null || searchFor == "") {
        emptyQuery = true;
      } else {
        //Pre-process the query to validate and send conditionally

        //get the discrete search terms and verify there are max two

        if (searchFor.includes(" ")) {
          queryTerms = searchFor.split(" ");
          if (queryTerms.length > 2) {
            vm.entryError = vm.$ay.t("ErrorPickListQueryInvalid");
            return;
          }
          isATwoTermQuery = true;
        } else {
          //one term only so push it into array
          queryTerms.push(searchFor);
          //Marker term, will be weeded back out later
          queryTerms.push("[?]");
        }

        //Now vet the terms
        //Is user in mid entry of a second tag (space only?)
        //will appear as an empty string post split
        if (queryTerms[1] == "") {
          //mid entry of a second term, just return
          return;
        }

        //Is user in mid entry of tag query, just bounce back
        if (
          queryTerms[0] == "." ||
          queryTerms[0] == ".." ||
          queryTerms[1] == "." ||
          queryTerms[1] == ".."
        ) {
          return;
        }

        if (isATwoTermQuery) {
          //check that both terms aren't tags
          if (
            //note: de-lodashed here not sure if I need to add a null check or not
            queryTerms[0].startsWith("..") &&
            queryTerms[1].startsWith("..")
          ) {
            vm.entryError = vm.$ay.t("ErrorPickListQueryInvalid");
            return;
          }

          //check that both aren't non-tags
          if (
            !queryTerms[0].startsWith("..") &&
            !queryTerms[1].startsWith("..")
          ) {
            vm.entryError = vm.$ay.t("ErrorPickListQueryInvalid");
            return;
          }
        }
      }

      //build parameter
      let pickListParams = { ayaType: vm.ayaType };
      //let urlParams = "?ayaType=" + vm.ayaType;
      if (!emptyQuery) {
        let query = queryTerms[0];
        if (queryTerms[1] != "[?]") {
          query += " " + queryTerms[1];
        }
        //urlParams += "&query=" + query;
        pickListParams["query"] = query;
      }

      if (vm.includeInactive) {
        pickListParams["inactive"] = true;
        //urlParams += "&inactive=true";
      }
      if (vm.variant != null) {
        pickListParams["listVariant"] = vm.variant;
        //urlParams += `&variant=${vm.variant}`;
      }
      this.getList(pickListParams);
      //------------
    }, 300) //did some checking, 200-300ms seems to be the most common debounce time for ajax search queries
  }
};

//https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_debounce
function debounce(func, wait, immediate) {
  var timeout;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(function() {
      timeout = null;
      if (!immediate) func.apply(context, args);
    }, wait);
    if (immediate && !timeout) func.apply(context, args);
  };
}
</script>
