<template>
	<div
		:class="{ 'is-loading': taskService.loading }"
		class="defer-task loading-container"
		@click.stop
		@mousedown.stop
		@pointerdown.stop
		@touchstart.stop
	>
		<label class="label">{{ $t('task.deferDueDate.title') }}</label>
		<div class="defer-days">
			<XButton
				:shadow="false"
				variant="secondary"
				@click.prevent.stop="() => deferDays(1)"
			>
				{{ $t('task.deferDueDate.1day') }}
			</XButton>
			<XButton
				:shadow="false"
				variant="secondary"
				@click.prevent.stop="() => deferDays(3)"
			>
				{{ $t('task.deferDueDate.3days') }}
			</XButton>
			<XButton
				:shadow="false"
				variant="secondary"
				@click.prevent.stop="() => deferDays(7)"
			>
				{{ $t('task.deferDueDate.1week') }}
			</XButton>
		</div>
		<flat-pickr
			v-model="dueDate"
			:class="{ disabled: taskService.loading }"
			:config="flatPickerConfig"
			:disabled="taskService.loading || undefined"
			class="input"
		/>
	</div>
</template>

<script setup lang="ts">
import {ref, shallowReactive, computed, watch, onMounted, onBeforeUnmount} from 'vue'
import {useI18n} from 'vue-i18n'
import flatPickr from 'vue-flatpickr-component'

import TaskService from '@/services/task'
import type {ITask} from '@/modelTypes/ITask'
import {useFlatpickrLanguage} from '@/helpers/useFlatpickrLanguage'
import {useTimeFormat} from '@/composables/useTimeFormat'
import {TIME_FORMAT} from '@/constants/timeFormat'

const props = defineProps<{
	modelValue: ITask,
}>()

const emit = defineEmits<{
	'update:modelValue': [value: ITask]
}>()

const {t} = useI18n({useScope: 'global'})
const {store: timeFormat} = useTimeFormat()

const taskService = shallowReactive(new TaskService())
const task = ref<ITask>()

// We're saving the due date separately to prevent null errors in very short periods where the task is null.
const dueDate = ref<Date | null>()
const lastValue = ref<Date | null>()
const changeInterval = ref<ReturnType<typeof setInterval>>()

watch(
	() => props.modelValue,
	(value) => {
		task.value = { ...value }
		dueDate.value = value.dueDate
		lastValue.value = value.dueDate
	},
	{immediate: true},
)

onMounted(() => {
	// Because we don't really have other ways of handling change since if we let flatpickr
	// change events trigger updates, it would trigger a flatpickr change event which would trigger
	// an update which would trigger a change event and so on...
	// This is either a bug in flatpickr or in the vue component of it.
	// To work around that, we're only updating if something changed and check each second and when closing the popup.
	if (changeInterval.value) {
		clearInterval(changeInterval.value)
	}

	changeInterval.value = setInterval(updateDueDate, 1000)
})

onBeforeUnmount(() => {
	if (changeInterval.value) {
		clearInterval(changeInterval.value)
	}
	updateDueDate()
})

const flatPickerConfig = computed(() => ({
	altFormat: t('date.altFormatLong'),
	altInput: true,
	dateFormat: 'Y-m-d H:i',
	enableTime: true,
	time_24hr: timeFormat.value === TIME_FORMAT.HOURS_24,
	inline: true,
	locale: useFlatpickrLanguage().value,
}))

function deferDays(days: number) {
	dueDate.value = new Date(dueDate.value)
	const currentDate = new Date(dueDate.value).getDate()
	dueDate.value = new Date(dueDate.value).setDate(currentDate + days)
	updateDueDate()
}

async function updateDueDate() {
	if (!dueDate.value) {
		return
	}

	if (+new Date(dueDate.value) === +lastValue.value) {
		return
	}

	const newTask = await taskService.update({
		...task.value,
		dueDate: new Date(dueDate.value),
	})
	lastValue.value = newTask.dueDate
	task.value = newTask
	emit('update:modelValue', newTask)
}
</script>

<style lang="scss" scoped>
// 100px is roughly the size the pane is pulled to the right
$defer-task-max-width: 350px + 100px;

.defer-task {
	inline-size: 100%;
	max-inline-size: $defer-task-max-width;

	@media screen and (max-width: ($defer-task-max-width)) {
		inset-inline-start: .5rem;
		inset-inline-end: .5rem;
		max-inline-size: 100%;
		inline-size: calc(100vw - 1rem - 2rem);
	}
}

.defer-days {
	justify-content: space-between;
	display: flex;
	margin: .5rem 0;
}

:deep() {
	input.input {
		display: none;
	}

	.flatpickr-calendar {
		margin: 0 auto;
		box-shadow: none;

		@media screen and (max-width: ($defer-task-max-width)) {
			max-inline-size: 100%;
		}

		span {
			inline-size: auto !important;
		}

	}

	.flatpickr-innerContainer {
		@media screen and (max-width: ($defer-task-max-width)) {
			overflow: scroll;
		}
	}
}
</style>
