<template>
    <MalioDrawer v-model="open" drawer-class="max-w-xl">
        <template #header>
            <h2 class="text-xl font-bold">{{ $t('absences.newRequest') }}</h2>
        </template>
        <div class="flex flex-col gap-5">
            <!-- Server-side error banner -->
            <div v-if="serverError" class="flex items-start gap-2 rounded-lg bg-red-50 p-3 text-sm text-red-700">
                <Icon name="mdi:alert-circle-outline" size="18" class="mt-0.5 flex-shrink-0" />
                <span>{{ serverError }}</span>
            </div>

            <!-- Step 1 — type (always visible) -->
            <MalioSelect
                v-model="form.type"
                :label="$t('absences.form.type')"
                :options="typeOptions"
                :empty-option-label="$t('absences.filters.allTypes')"
                :error="errors.type"
                group-class="w-full"
            />

            <!-- Step 2 — start date (revealed once a type is chosen) -->
            <AbsenceDateField
                v-if="showDates"
                v-model="form.startDate"
                v-model:half="form.startHalf"
                :label="$t('absences.form.startDate')"
                mode="start"
                :error="errors.startDate"
                :max="form.endDate"
            />

            <!-- Balance at start date -->
            <div v-if="preview && preview.available !== null" class="flex items-center justify-between border-t border-neutral-100 pt-3 text-sm">
                <span class="font-medium text-neutral-700">{{ $t('absences.form.balanceAt', { date: startDateLabel }) }}</span>
                <span class="text-neutral-900">{{ formatDays(preview.available) }}</span>
            </div>

            <!-- Step 3 — end date (revealed once a start date is set) -->
            <AbsenceDateField
                v-if="showEnd"
                v-model="form.endDate"
                v-model:half="form.endHalf"
                :label="$t('absences.form.endDate')"
                mode="end"
                :error="errors.endDate"
                :min="form.startDate"
                :show-pills="!isSingleDay"
            />

            <!-- Duration & projected balance -->
            <div v-if="preview" class="flex flex-col gap-1 rounded-lg bg-neutral-50 p-3">
                <div class="flex items-center justify-between text-sm">
                    <span class="text-neutral-600">{{ $t('absences.form.duration') }}</span>
                    <span class="font-semibold text-neutral-900">{{ formatDays(preview.countedDays) }}</span>
                </div>
                <div v-if="preview.projectedAvailable !== null" class="flex items-center justify-between border-t border-neutral-200 pt-1 text-sm">
                    <span class="font-medium text-neutral-700">{{ $t('absences.form.balanceAfterValidation') }}</span>
                    <span :class="preview.projectedAvailable < 0 ? 'font-semibold text-amber-600' : 'text-neutral-900'">
                        {{ formatDays(preview.projectedAvailable) }}
                    </span>
                </div>
            </div>
            <div
                v-if="preview && preview.projectedAvailable !== null && preview.projectedAvailable < 0"
                class="rounded-lg bg-amber-50 p-3 text-sm text-amber-700"
            >
                {{ $t('absences.form.negativeWarning') }}
            </div>

            <!-- Step 4 — justification (only when the policy requires it) -->
            <MalioInputUpload
                v-if="showJustification"
                :model-value="form.file?.name ?? null"
                :label="`${$t('absences.form.justification')} *`"
                accept="application/pdf,image/png,image/jpeg,image/webp"
                :error="errors.justification"
                @file-selected="onFileSelected"
            />

            <!-- Comment (optional) -->
            <div v-if="showComment" class="flex items-start gap-2">
                <span class="mt-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary-100 text-xs font-semibold text-primary-600">
                    {{ initials }}
                </span>
                <MalioInputTextArea
                    v-model="form.reason"
                    group-class="flex-1"
                    :placeholder="$t('absences.form.commentPlaceholder')"
                />
            </div>

            <!-- Footer -->
            <div class="flex justify-end gap-3 pt-2">
                <MalioButton :label="$t('common.cancel')" variant="tertiary" @click="open = false" />
                <MalioButton
                    :label="$t('absences.form.submit')"
                    :disabled="submitting"
                    @click="submit"
                />
            </div>
        </div>
    </MalioDrawer>
</template>

<script setup lang="ts">
import type { AbsencePolicy, AbsencePreviewResult, AbsenceType, HalfDay } from '~/modules/absence/services/dto/absence'
import { useAbsenceService } from '~/modules/absence/services/absences'

const props = defineProps<{
    modelValue: boolean
    policies: AbsencePolicy[]
}>()

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

const { t } = useI18n()
const { formatDays, formatDate } = useAbsenceHelpers()
const service = useAbsenceService()
const auth = useAuthStore()

const open = computed({
    get: () => props.modelValue,
    set: (v) => emit('update:modelValue', v),
})

type FormState = {
    type: AbsenceType | null
    // ISO date strings "YYYY-MM-DD" (lexicographic order == chronological order).
    startDate: string | null
    startHalf: HalfDay | null
    endDate: string | null
    endHalf: HalfDay | null
    reason: string
    file: File | null
}

const form = reactive<FormState>({
    type: null,
    startDate: null,
    startHalf: null,
    endDate: null,
    endHalf: null,
    reason: '',
    file: null,
})

const errors = reactive<{ type: string; startDate: string; endDate: string; justification: string }>({
    type: '',
    startDate: '',
    endDate: '',
    justification: '',
})

const serverError = ref('')
const preview = ref<AbsencePreviewResult | null>(null)
const submitting = ref(false)

const typeOptions = computed(() =>
    props.policies
        .filter(p => p.active)
        .map(p => ({ label: p.label, value: p.type })),
)

const selectedPolicy = computed(() => props.policies.find(p => p.type === form.type) ?? null)
const justificationRequired = computed(() => selectedPolicy.value?.justificationRequired ?? false)

const showDates = computed(() => form.type !== null)
const showEnd = computed(() => form.startDate !== null)
const showJustification = computed(() => form.type !== null && justificationRequired.value)
const showComment = computed(() => form.startDate !== null)

const isSingleDay = computed(() =>
    form.startDate !== null
    && form.endDate !== null
    && form.startDate === form.endDate,
)

const startDateLabel = computed(() => formatDate(form.startDate))

const initials = computed(() => {
    const name = auth.user?.username ?? ''
    return name.slice(0, 2).toUpperCase() || '?'
})

function onFileSelected(file: File) {
    form.file = file
    errors.justification = ''
}

function buildPayload() {
    // On a single-day request the end half-day mirrors the start.
    const endHalf = isSingleDay.value ? form.startHalf : form.endHalf
    return {
        type: form.type as AbsenceType,
        startDate: form.startDate as string,
        endDate: form.endDate as string,
        startHalfDay: form.startHalf,
        endHalfDay: endHalf,
        reason: form.reason || null,
    }
}

function validate(): boolean {
    errors.type = form.type ? '' : t('absences.form.errors.typeRequired')
    errors.startDate = form.startDate ? '' : t('absences.form.errors.startRequired')

    if (form.endDate === null) {
        errors.endDate = t('absences.form.errors.endRequired')
    } else if (form.startDate && form.endDate < form.startDate) {
        errors.endDate = t('absences.form.errors.endBeforeStart')
    } else if (form.type && form.startDate && (preview.value?.countedDays ?? 0) <= 0) {
        errors.endDate = t('absences.form.errors.zeroDays')
    } else {
        errors.endDate = ''
    }

    errors.justification = justificationRequired.value && !form.file
        ? t('absences.form.errors.justificationRequired')
        : ''

    return !errors.type && !errors.startDate && !errors.endDate && !errors.justification
}

// Clear field errors as soon as the user corrects them.
watch(() => form.type, (v) => { if (v) errors.type = '' })
watch(() => form.startDate, (v) => { if (v) errors.startDate = '' })
watch(() => [form.endDate, form.startDate], () => {
    if (form.endDate && (!form.startDate || form.endDate >= form.startDate)) errors.endDate = ''
})

let debounceTimer: ReturnType<typeof setTimeout> | null = null

watch(
    () => [form.type, form.startDate, form.endDate, form.startHalf, form.endHalf],
    () => {
        if (debounceTimer) clearTimeout(debounceTimer)
        if (!form.type || !form.startDate || !form.endDate) {
            preview.value = null
            return
        }
        debounceTimer = setTimeout(async () => {
            try {
                preview.value = await service.preview(buildPayload())
            } catch {
                preview.value = null
            }
        }, 300)
    },
    { deep: true },
)

async function submit() {
    serverError.value = ''
    if (!validate()) return
    submitting.value = true
    try {
        const created = await service.create(buildPayload())
        if (form.file) {
            await service.uploadJustification(created.id, form.file)
        }
        emit('created')
        open.value = false
        resetForm()
    } catch (e) {
        serverError.value = (e instanceof Error && e.message) ? e.message : t('absences.form.serverError')
    } finally {
        submitting.value = false
    }
}

function resetForm() {
    form.type = null
    form.startDate = null
    form.startHalf = null
    form.endDate = null
    form.endHalf = null
    form.reason = ''
    form.file = null
    errors.type = ''
    errors.startDate = ''
    errors.endDate = ''
    errors.justification = ''
    serverError.value = ''
    preview.value = null
}

watch(open, (v) => {
    if (v) resetForm()
})
</script>
