<template>
	<div>
		<label class="block text-sm mb-1 text-gray-700" :class="{ 'opacity-50': disabled, 'text-red-600': error }">
			{{ label }}
		</label>
		<div class="block relative">
			<input :type="type" :placeholder="placeholder" v-model="value" ref="textbox" :disabled="disabled"
				class="w-full outline-none p-2 rounded-lg bg-gray-100 border border-gray-100 text-gray-800 placeholder-gray-400 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50 disabled:border-gray-400"
				:class="{ 'pr-10': (isValid || error), 'border-red-600 text-red-600': error }"
				@blur="onBlur" @change="onChange" @input="setErrorMessage('')">
			<span class="w-10 absolute flex items-center top-0 bottom-0 right-0 justify-center" :class="{ 'hidden': !error && !isValid }">
				<i v-if="error" class="fas fa-exclamation-circle align-middle text-red-600"></i>
				<i v-else-if="isValid" class="fas fa-check-circle align-middle text-green-500"></i>
			</span>
		</div>
		<div class="w-full error-message text-xs text-red-600 text-center" ref="error">{{ error }}</div>
	</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { validateValue } from "../util";

export default defineComponent({
	emits: ["onBlur", "onChange", "update:modelValue", "update:errorValue"],
	data() {
		return {
			error: "",
			isValid: <boolean|undefined> this.valid,
			value: this.modelValue
		}
	},
	methods: {
		/**
		 * Invoked when the input has lost focus
		 */
		onBlur(event: Event) {
			this.$emit("onBlur", event);
			this.validate();
		},

		/**
		 * Invoked when the value of the input has changed
		 */
		onChange(event: Event) {
			this.$emit("update:modelValue", this.value);
			this.$emit("onChange", event);
		},

		/**
		 * Set the displayed error message
		 */
		setErrorMessage(value: string) {
			this.error = value;
			this.$emit("update:errorValue", value);
			if (value) {
				this.isValid = false;
			}
		},

		/**
		 * Validate the current input
		 */
		async validate() {
			this.isValid = false;
			if (this.validator !== undefined) {
				if (typeof this.validator === "function") {
					this.error = (await this.validator(this.value)) ?? "";
				} else {
					this.error = validateValue(this.value, this.validator);
				}
				if (!this.error) {
					this.isValid = true;
				}
			}
		}
	},
	mounted() {
		if (this.value && this.validator) {
			this.validate();
		}
	},
	props: {
		disabled: {
			type: Boolean,
			default: false
		},
		errorValue: {
			type: String,
			default: ""
		},
		label: String,
		modelValue: {
			type: String,
			default: ""
		},
		placeholder: {
			type: String,
			default: ""
		},
		required: [Boolean, String],
		valid: {
			type: Boolean,
			default: undefined
		},
		type: {
			type: String,
			default: "text"
		},
		validator: [Function, Object]
	}
});
</script>
