<template>
	<div class="rmc_criteria_form_authority">
		<fieldvars v-if="globals.rmc_responsive == 2" :fields="criteria.VAR" :fieldId="criteria.FIELD_ID" :index="index" :searchData="searchData"/>
		<operators v-if="criteria.QUERIES && globals.rmc_responsive == 1" 
			:fieldId="criteria.FIELD_ID" 
			:index="index" 
			:queries="criteria.QUERIES" 
			:selected="selectedOp" 
			@changeOp=" e => changeOperator(e)"
			:criteriaTitle="criteria.TITLE">
		</operators>
			
		<div class="rmc_search_authority_container">
			<operators v-if="criteria.QUERIES && globals.rmc_responsive == 2" 
				:fieldId="criteria.FIELD_ID" 
				:index="index" 
				:queries="criteria.QUERIES" 
				:selected="selectedOp" 
				@changeOp=" e => changeOperator(e)"
				:criteriaTitle="criteria.TITLE"
				:inputType="criteria.INPUT_TYPE">
			</operators>
			<input v-if="selectedOp != authorityOp" :id="id + '_id_0'" :name="id + '[]'"  type="hidden" v-model="searchValue">
			<input v-else :id="id + '_id_0'" :name="id + '[]'" type="hidden" v-model="searchValueId">
			<div :id="'d' + id + '_lib_' + index" class="ajax_completion" ></div>
			<div class="rmc_search_authority_autocomplete">
				<input
					:id="id + '_lib_' + index"
					:name="name"
					class="rmc_search_authority rmc_search_txt rmc_search_focusable"
					type="text"
					autocomplete="off"
					:list="id + '_lib_' + index + '_datalist'"
					:aria-label="pmb.getMessage('searchform', 'value_for_criteria_authority') + criteria.TITLE"
					v-model="searchValue"
					@input.prevent="updateDataList"
					@keydown.down.prevent="increaseIndex"
					@keydown.up.prevent="decreaseIndex"
					@keydown.shift.tab="handleShiftTab"
					@keydown.esc="hideDatalist(true)"
					@keydown.enter="handleEnter">
					<!-- L'event blur provoque des problemes avec le click gauche enfonce -->
					<!-- @blur="hideDatalist(true)">-->
		
				<ul v-if="dataListDisplayed" :id="id + '_lib_' + index + '_datalist'" class="rmc_datalist" role="listbox">
					<li v-for="(element, index) in dataList" :key="index"
						:class="`rmc_datalist_option ${index == dataListIndex ? 'rmc_datalist_option_active' : ''}`" 
						:data-entity_id="element.value"
						@click.self="selectElement(index); hideDatalist(false)"
						:aria-selected="index == dataListIndex"
						role="option">
						<div 
							:title="element.label" 
							class="rmc_datalist_label"
							@click.self="selectElement(index); hideDatalist(false)">
							{{ element.label }}
						</div>
					</li>
				</ul>
			</div>
			<fieldvars v-if="globals.rmc_responsive == 1" :fields="criteria.VAR" :fieldId="criteria.FIELD_ID" :index="index" :searchData="searchData"/>
		</div>
    </div>
</template>
<script>
import fieldvars from "./fieldvars.vue";
import operators from "./operators.vue";

export default {
	name: "criteriaFormAuthority",
	props : ['criteria', 'searchData', 'index', 'showfieldvars', 'globals'],
	data: function () {
		return {
			selectorValue: "",
			searchValue: "",
			dataList: [],
			dataListIndex: -1,
			dataListDisplayed: false,
			selectedOp: '',
	        searchValueId: "",
			authorityOp : '',
			booleanOp : ''
		}
	},
	components : {
	    fieldvars,
	    operators,
	},
	
	watch: {
		criteria: function(newVal,oldVal) {
			this.initOperators();
			this.clearValue();
		}
	},
	created : function() {
		this.initOperators();
    	if ("undefined" !== typeof this.searchData[this.index] && (this.searchData[this.index].SEARCH == this.criteria.FIELD_ID)) {
	    	if(this.searchData[this.index] && this.searchData[this.index].OP){
	            for (var i = 0; i < this.criteria.QUERIES.length; i++) {
	                var operator = this.criteria.QUERIES[i];
	                if (this.searchData[this.index].OP == operator['OPERATOR']) {
	                	this.selectedOp = this.searchData[this.index].OP;
	                }
	            }
	    	}
		}
       	if(this.searchData[this.index] && this.searchData[this.index].FIELD){
	       	if(this.searchData[this.index] && this.searchData[this.index].FIELDLIB){
	       		this.searchValue = this.searchData[this.index].FIELDLIB[0];
	       	} else {
	       		this.searchValue = this.searchData[this.index].FIELD[0];
	       	}
	       	if(this.selectedOp == this.authorityOp){
	       		this.searchValueId = this.searchData[this.index].FIELD[0];
				this.searchValue = this.criteria.VALUES[this.index].label;
	       	}
       	}

		this.initListeners();
	},
	computed: {
        name: function() {
            return `field_${this.index}_${this.criteria.FIELD_ID}_lib[]`;
        },
        autfield: function() {
        	return `field_${this.index}_${this.criteria.FIELD_ID}_id_0`;
        },
        autid: function() {
        	return `field_${this.index}_${this.criteria.FIELD_ID}_id_0`;
        },
        id: function() {
        	return `field_${this.index}_${this.criteria.FIELD_ID}`;
        },
        opName: function() {
        	return `op_${this.index}_${this.criteria.FIELD_ID}`;
        },
    },
	mounted: function() {
		this.authoritiesAjaxParse(this.criteria.INPUT_TYPE);
		document.addEventListener('click', this.handleClickOutside);
	},
	beforeDestroy() {
        document.removeEventListener('click', this.handleClickOutside);
    },
	methods: {
		/**
		 * Parse le DOM a l'aide d'une fonction AJAX.
		 * @returns {void}
		 */
		authoritiesAjaxParse() {
			ajax_parse_dom();
		},

		/**
		 * Initialise les ecouteurs d'evenements pour le formulaire.
		 * Ecoute l'evenement 'beforeSubmit' et met a jour l'operateur selectionne.
		 * @returns {void}
		 */
		initListeners() {
			this.$root.$on("beforeSubmit", () => {
				let input = document.getElementById(this.id+'_id_0');
				if(input != null) {
					if(input.value == ""){
						//Si on n'a pas recupere l'id de l'autorite
						this.selectedOp = this.booleanOp;
					}
				}
			})
		},

		/**
		 * Selectionne l'element dans la liste.
		 * @param {number} index - L'index de l'element e selectionner. Par defaut, -1 (aucun).
		 * @returns {void}
		 */
		selectElement(index = -1) {
			// Si un index est fourni (different de -1), met a jour l'index de la datalist
			if (index !== -1) {
				this.dataListIndex = index;
			}

			const selectedElement = this.dataList[this.dataListIndex];
			// Verifie si l'element selectionne existe
			if (selectedElement && selectedElement.value) {

				// Met a jour les champs avec les informations de l'element selectionne
				this.$set(this, "selectedOp", this.authorityOp);
				this.$set(this, "searchValueId", selectedElement.value);
				this.$set(this, "searchValue", selectedElement.label);
			}
		},

		/**
		 * Met a jour la liste de donnees en effectuant une requete AJAX.
		 * @returns {void}
		 */
		updateDataList() {
			// Verifie si l'operateur selectionne est 'AUTHORITY'
			if (this.selectedOp == this.authorityOp) {
				this.$set(this, "selectedOp", this.booleanOp);
				this.$set(this, "searchValueId", 0);
			}
			// Utilise la methode delay (debounce) pour limiter la frequence des requetes
			this.delay(() => {
				// Creation d'un FormData pour envoyer les donnees via AJAX
				const formData = new FormData();
				// Ajout de la requete
				formData.append("handleAs", "json");
				formData.append("completion", this.criteria.INPUT_OPTIONS.AJAX);
				formData.append("autexclude", "");
				formData.append("param1", "");
				formData.append("param2", 1);
				formData.append("rmc_responsive", 1);
				if (this.criteria.LINKFIELD) {
					formData.append("linkfield", this.getLinkField());
				}
				// Recupere la valeur saisie, si vide utilise * pour rechercher tous les resultats
				const data = this.searchValue.trim() || "*";
				formData.append("datas", data);
				// Effectue la requete fetch avec les donnees
				fetch("./ajax_selector.php", {
					method: 'POST',
					body: formData
				})
				.then(response => {
					// Si la reponse est valide, on la parse en JSON et on met a jour la dataList
					if (response.ok) {
						return response.json();
					} else {
						throw new Error("Erreur lors de la requete AJAX");
					}
				})
				.then(result => {
					// Met a jour la liste des suggestions avec les resultats renvoyes par le serveur
					this.setDatalist(result);
				})
				.catch(error => {
					console.error("Erreur AJAX:", error.message);
				});
			}, 600); // Delai en ms
		},

		/**
		 * Limite la frequence d'execution d'une fonction.
		 * @param {Function} func - La fonction e executer apres le delai.
		 * @param {number} wait - Le delai en ms.
		 * @returns {void}
		 */
		delay(func, wait) {
            clearTimeout(this.debounceTimeout);
            this.debounceTimeout = setTimeout(func, wait);
        },

		/**
		 * Met a jour la liste de suggestions avec les donnees.
		 * @param {Array} data - La nouvelle liste de donnees.
		 * @returns {void}
		 */
		setDatalist(data) {
			// Met a jour la liste de suggestions
			this.dataList = data;
			this.dataListIndex = -1;

			// Si la liste de suggestions contient des elements, affiche la datalist
			if(this.dataList.length > 0) {
				this.displayDatalist();
				return;
			}

			// Sinon, masque la datalist
			this.hideDatalist();
		},

		/**
		 * Incremente l'index de l'element selectionne dans la datalist.
		 * @returns {void}
		 */
		increaseIndex() {
			// Verifie si la liste est vide ou non affichee
			if (this.dataList.length === 0 || !this.dataListDisplayed) {

				// Met a jour la datalist si necessaire
				this.updateDataList();
				return;
			}

			// Verifie que l'index actuel ne depasse pas la longueur de la liste
			if (this.dataListIndex + 1 < this.dataList.length) {
				this.dataListIndex++;

				// Affiche et met a jour l'element selectionne
				this.displayDatalist();
				this.updateFocus();
				this.selectElement();
			}
		},

		/**
		 * Decremente l'index de l'element selectionne dans la datalist.
		 * @returns {void}
		 */
		decreaseIndex() {
			// Verifie si la liste est vide, si l'index est deje e 0 ou si la liste est cachee
			if (this.dataList.length === 0 || this.dataListIndex === 0 || !this.dataListDisplayed) {
				return;
			}

			this.dataListIndex--;

			// Affiche et met a jour l'element selectionne
			this.displayDatalist();
			this.updateFocus();
			this.selectElement();
		},

		/**
		 * Reinitialise l'index de la liste de donnees e -1.
		 * @returns {void}
		 */
		resetIndex() {
			this.dataListIndex = -1;
		},

		/**
		 * Affiche la liste des suggestions.
		 * @returns {void}
		 */
		displayDatalist() {
			this.dataListDisplayed = true;
		},

		/**
		 * Gere l'evenement d'appui sur la touche Entree.
		 * Selectionne l'element et masque la liste de donnees.
		 * @param {Event} event
		 * @returns {void}
		 */
		handleEnter(event) {
			// Si la liste des donnees n'est pas affichee et qu'aucun element n'est selectionne
			if (!this.dataListDisplayed && this.dataListIndex === -1) {
				return;
			}

			// Empeche le comportement par defaut du navigateur
			event.preventDefault();

			// Selectionne l'element actuel
			this.selectElement();

			// Masque la datalist
			this.hideDatalist();
		},

		/**
		 * Masque la liste de donnees.
		 * Optionnellement, attend un delai avant de la masquer.
		 * @param {boolean} cooldown
		 * @returns {void}
		 */
		hideDatalist(cooldown = false) {
			// Si on a un delai, on attend un peu avant de masquer la datalist
			if (cooldown) {
				setTimeout(() => {
					// Masque la datalist
					this.dataListDisplayed = false;
				}, 140);
			} else {
				// Masque immediatement la datalist
				this.dataListDisplayed = false;
			}

			// Reinitialise l'index de selection
			this.resetIndex();
		},

		/**
		 * Met a jour le focus sur l'element actuellement selectionne dans la liste de donnees.
		 * @returns {void}
		 */
		updateFocus() {
			// Si la liste n'est pas affichee
			if (!this.dataListDisplayed) {
				return;
			}
			const customDatalist = document.querySelector('ul.rmc_datalist');
			// S'assure que la datalist existe avant de continuer
			if (!customDatalist) {
				return;
			}
			const listItems = customDatalist.querySelectorAll('li.rmc_datalist_option');

			listItems.forEach((item, index) => {
				if (index === this.dataListIndex) {
					// Scroll pour rendre visible l'element actif
					item.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
				}
			});
		},

		/**
		 * Gere les clics en dehors de la liste de donnees pour masquer la liste.
		 * @param {Event} event
		 * @returns {void}
		 */
		handleClickOutside(event) {
			// Recupere la datalist et l'input dans le composant courant
			const datalist = this.$el.querySelector('.rmc_datalist');
			const input = this.$el.querySelector('input.rmc_search_authority');

			// Verifie si la datalist et l'input existent
			if (datalist && input) {
				// Verifie si le clic a eu lieu en dehors de la datalist et de l'input
				const isClickOutside = !datalist.contains(event.target) && !input.contains(event.target);

				// Si le clic est en dehors on cache la datalist
				if (isClickOutside) {
					this.hideDatalist();
				}
			}
		},
		 
		/**
		 * methode appelee par le composant enfant pour mettre a jour l'operateur selectionne.
		 * @param array data
		 * @returns {void}
		 */
		changeOperator(data) {
			if(data[1] == this.index){
				this.selectedOp = data[0];
			}
		},
		
		/**
		 * recupere le champ lie
		 * @returns string
		 */
		getLinkField() {
        	if (this.criteria.LINKFIELD) {
        		let linffieldNode = document.getElementById(`fieldvar_${this.index}_${this.criteria.FIELD_ID}_${this.criteria.LINKFIELD}_0`);
        		if (linffieldNode && linffieldNode.value) {
        			return linffieldNode.value;
        		}
        	}
        	return "";
		},
		/**
		 * supprime la valeur saisie
		 * @returns {void}
		 */
		clearValue() {
			this.searchValue = "";
			this.searchValueId = "";
			//this.updateFocus();
		},
		/**
		 * Initialise les operateurs pour le formulaire.
		 * L'operateur 'AUTHORITY' est selectionne par defaut.
		 * Si le type d'input est 'query_auth', l'operateur 'EQ' est selectionne par defaut.
		 * @returns {void}
		 */
		handleShiftTab() {
			if (this.dataListDisplayed) {
				this.hideDatalist();
			}
			// Pas de preventDefault : le navigateur gere le retour focus naturellement
		},
		initOperators() {
			this.authorityOp = 'AUTHORITY';
			this.booleanOp = 'BOOLEAN';
			if (this.criteria.INPUT_TYPE == 'query_auth') {
				this.authorityOp = this.booleanOp ='EQ';
			}
			this.selectedOp = this.authorityOp;
		}
	}
}
</script>