<template>
	<li class="question__item">
		<input
			ref="input"
			:aria-label="t('forms', 'An answer for the {index} option', { index: index + 1 })"
			:placeholder="t('forms', 'Answer number {index}', { index: index + 1 })"
			:value="answer.text"
			class="question__input"
			:maxlength="maxOptionLength"
			minlength="1"
			type="text"
			@input="onInput"
			@keydown.delete="deleteEntry"
			@keydown.enter.prevent="addNewEntry">

		<!-- Delete answer -->
		<Actions>
			<ActionButton icon="icon-close" @click="deleteEntry">
				{{ t('forms', 'Delete answer') }}
			</ActionButton>
		</Actions>
	</li>
</template>

<script>
import { showError } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import pDebounce from 'p-debounce'
import PQueue from 'p-queue'

import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'

export default {
	name: 'AnswerInput',

	components: {
		Actions,
		ActionButton,
	},

	props: {
		answer: {
			type: Object,
			required: true,
		},
		index: {
			type: Number,
			required: true,
		},
		maxOptionLength: {
			type: Number,
			required: true,
		},
	},

	data() {
		return {
			queue: new PQueue({ concurrency: 1 }),

			// As data instead of Method, to have a separate debounce per AnswerInput
			debounceUpdateAnswer: pDebounce(function(answer) {
				return this.queue.add(() => this.updateAnswer(answer))
			}, 500),
		}
	},

	methods: {
		/**
		 * Focus the input
		 */
		focus() {
			this.$refs.input.focus()
		},

		/**
		 * Option changed, processing the data
		 */
		async onInput() {
			// clone answer
			const answer = Object.assign({}, this.answer)
			answer.text = this.$refs.input.value

			if (this.answer.local) {

				// Dispatched for creation. Marked as synced
				this.answer.local = false
				const newAnswer = await this.debounceCreateAnswer(answer)

				// Forward changes, but use current answer.text to avoid erasing
				// any in-between changes while creating the answer
				Object.assign(newAnswer, { text: this.$refs.input.value })
				this.$emit('update:answer', answer.id, newAnswer)
			} else {
				this.debounceUpdateAnswer(answer)
				this.$emit('update:answer', answer.id, answer)
			}
		},

		/**
		 * Request a new answer
		 */
		addNewEntry() {
			this.$emit('add')
		},

		/**
		 * Emit a delete request for this answer
		 * when pressing the delete key on an empty input
		 *
		 * @param {Event} e the event
		 */
		async deleteEntry(e) {
			if (e.type !== 'click' && this.$refs.input.value.length !== 0) {
				return
			}

			// Dismiss delete key action
			e.preventDefault()

			this.$emit('delete', this.answer.id)
		},

		/**
		 * Create an unsynced answer to the server
		 *
		 * @param {Object} answer the answer to sync
		 * @returns {Object} answer
		 */
		async createAnswer(answer) {
			try {
				const response = await axios.post(generateUrl('/apps/forms/api/v1/option'), {
					questionId: answer.question_id,
					text: answer.text,
				})
				console.debug('Created answer', answer)

				// Was synced once, this is now up to date with the server
				delete answer.local
				return Object.assign({}, answer, response.data)
			} catch (error) {
				showError(t('forms', 'Error while saving the answer'))
				console.error(error)
			}

			return answer
		},
		debounceCreateAnswer: pDebounce(function(answer) {
			return this.queue.add(() => this.createAnswer(answer))
		}, 100),

		/**
		 * Save to the server, only do it after 500ms
		 * of no change
		 *
		 * @param {Object} answer the answer to sync
		 */
		async updateAnswer(answer) {
			try {
				await axios.post(generateUrl('/apps/forms/api/v1/option/update'), {
					id: this.answer.id,
					keyValuePairs: {
						text: answer.text,
					},
				})
				console.debug('Updated answer', answer)
			} catch (error) {
				showError(t('forms', 'Error while saving the answer'))
				console.error(error)
			}
		},
	},
}
</script>
