<template>
    <div>
        <div class="flex flex-wrap items-center justify-between gap-4">
            <h1 class="text-4xl font-bold text-primary-500">Calendrier des absences</h1>
        </div>
        <div class="flex items-center justify-between py-6">
            <div class="flex items-center gap-4">
                <div class="flex flex-wrap items-center gap-4 rounded-md border border-neutral-300 px-3 py-2">
                    <div v-for="site in sites" :key="site.id" class="flex items-center gap-2">
                        <div :style="{ backgroundColor: site.color }" class="h-4 w-4 rounded"></div>
                        <label class="text-md" :for="`site-${site.id}`">{{ site.name }}</label>
                        <input
                            :id="`site-${site.id}`"
                            v-model="selectedSiteIds"
                            :value="site.id"
                            type="checkbox"
                            class="h-4 w-4"
                        />
                    </div>
                </div>
                <select
                    v-model="selectedMonth"
                    class="h-10 rounded-md border border-neutral-300 bg-white px-3 text-md text-neutral-900"
                >
                    <option v-for="month in months" :key="month.value" :value="month.value">
                        {{ month.label }}
                    </option>
                </select>
                <select
                    v-model="selectedYear"
                    class="h-10 rounded-md border border-neutral-300 bg-white px-3 text-md text-neutral-900"
                >
                    <option v-for="year in years" :key="year" :value="year">
                        {{ year }}
                    </option>
                </select>
            </div>
            <div class="flex gap-4">
                <button
                    type="button"
                    class="h-10 rounded-lg bg-primary-500 px-4 text-md font-semibold text-white hover:bg-secondary-500"
                    @click="openCreateFromToday"
                >
                    Ajouter une absence
                </button>
                <button
                    type="button"
                    class="h-10 rounded-lg bg-primary-500 px-4 text-md font-semibold text-white hover:bg-secondary-500"
                    @click="openPrint"
                >
                    Imprimer
                </button>
            </div>
        </div>

        <CalendarGrid
            :days-in-month="daysInMonth"
            :visible-employees="visibleEmployees"
            :grid-style="gridStyle"
            :get-cell-style="getCellStyle"
            :get-cell-code="getCellCode"
            :format-employee-name="formatEmployeeName"
            :is-holiday-date="isHolidayDate"
            @cell-click="openCreate"
        />

        <AbsenceFormDrawer
            v-model="isDrawerOpen"
            :employees="employees"
            :absence-types="absenceTypes"
            :form="form"
            :editing-absence="editingAbsence"
            :is-submitting="isSubmitting"
            @submit="handleSubmit"
            @delete="handleDelete"
            @cancel="closeDrawer"
        />

        <AbsencePrintDrawer
            v-model="isPrintOpen"
            :sites="sites"
            :print-form="printForm"
            @submit="handlePrint"
            @cancel="closePrint"
        />
    </div>
</template>

<script setup lang="ts">
import type {Employee} from '~/services/dto/employee'
import type {AbsenceType} from '~/services/dto/absence-type'
import type {Absence} from '~/services/dto/absence'
import {listEmployees} from '~/services/employees'
import {listAbsenceTypes} from '~/services/absence-types'
import {createAbsence, deleteAbsence, listAbsences, updateAbsence} from '~/services/absences'
import {listPublicHolidays} from '~/services/public-holidays'
import {getDaysInMonth, normalizeDate, toYmd} from '~/utils/date'
import CalendarGrid from '~/components/CalendarGrid.vue'
import AbsenceFormDrawer from '~/components/AbsenceFormDrawer.vue'
import AbsencePrintDrawer from '~/components/AbsencePrintDrawer.vue'

const employees = ref<Employee[]>([])
const sites = computed(() => {
    const siteMap = new Map<number, { id: number; name: string; color: string }>()
    for (const employee of employees.value) {
        if (employee.site) {
            siteMap.set(employee.site.id, employee.site)
        }
    }
    return Array.from(siteMap.values()).sort((siteA, siteB) => siteA.name.localeCompare(siteB.name, 'fr'))
})

const selectedSiteIds = ref<number[]>([])
const sitesInitialized = ref(false)

watch(sites, (next) => {
    if (sitesInitialized.value || next.length === 0) return
    selectedSiteIds.value = next.map((site) => site.id)
    sitesInitialized.value = true
}, { immediate: true })

const sortedEmployees = computed(() => {
  return [...employees.value].sort((employeeA, employeeB) => {
    const siteNameA = employeeA.site?.name ?? ''
    const siteNameB = employeeB.site?.name ?? ''
    if (siteNameA !== siteNameB) return siteNameA.localeCompare(siteNameB, 'fr')
        const lastNameA = employeeA.lastName ?? ''
        const lastNameB = employeeB.lastName ?? ''
        if (lastNameA !== lastNameB) return lastNameA.localeCompare(lastNameB, 'fr')
        const firstNameA = employeeA.firstName ?? ''
        const firstNameB = employeeB.firstName ?? ''
    return firstNameA.localeCompare(firstNameB, 'fr')
  })
})

const visibleEmployees = computed(() => {
    if (selectedSiteIds.value.length === 0) return []
    return sortedEmployees.value.filter((employee) => {
        return employee.site?.id && selectedSiteIds.value.includes(employee.site.id)
    })
})
const absenceTypes = ref<AbsenceType[]>([])
const absences = ref<Absence[]>([])
const publicHolidays = ref<Record<string, string>>({})

const isDrawerOpen = ref(false)
const isSubmitting = ref(false)
const editingAbsence = ref<Absence | null>(null)
const isPrintOpen = ref(false)

const now = new Date()
const selectedMonth = ref(now.getMonth())
const selectedYear = ref(now.getFullYear())

const months = [
    {value: 0, label: 'Janvier'},
    {value: 1, label: 'Février'},
    {value: 2, label: 'Mars'},
    {value: 3, label: 'Avril'},
    {value: 4, label: 'Mai'},
    {value: 5, label: 'Juin'},
    {value: 6, label: 'Juillet'},
    {value: 7, label: 'Août'},
    {value: 8, label: 'Septembre'},
    {value: 9, label: 'Octobre'},
    {value: 10, label: 'Novembre'},
    {value: 11, label: 'Décembre'}
]

const years = Array.from({length: 5}, (unusedValue, index) => now.getFullYear() - 2 + index)


const daysInMonth = computed(() => getDaysInMonth(selectedYear.value, selectedMonth.value))
const monthStartDate = computed(() => new Date(selectedYear.value, selectedMonth.value, 1))
const monthEndDate = computed(() => new Date(selectedYear.value, selectedMonth.value + 1, 0))

const gridStyle = computed(() => ({
    gridTemplateColumns: `160px repeat(${daysInMonth.value.length}, minmax(44px, 1fr))`
}))

const form = reactive({
    employeeId: '' as number | '',
    typeId: '' as number | '',
    startDate: '',
    endDate: '',
    comment: ''
})

const printForm = reactive({
    from: '',
    to: '',
    siteIds: [] as number[]
})


const resetForm = () => {
    form.employeeId = ''
    form.typeId = ''
    form.startDate = ''
    form.endDate = ''
    form.comment = ''
}

const closeDrawer = () => {
    isDrawerOpen.value = false
    editingAbsence.value = null
    resetForm()
}

const openPrint = () => {
    const monthStart = toYmd(selectedYear.value, selectedMonth.value, 1)
    const monthEnd = toYmd(selectedYear.value, selectedMonth.value + 1, 0)
    printForm.from = monthStart
    printForm.to = monthEnd
    printForm.siteIds = [...selectedSiteIds.value]
    isPrintOpen.value = true
}

const closePrint = () => {
    isPrintOpen.value = false
}

const parseYmd = (value: string) => {
    const [year, month, day] = value.split('-').map(Number)
    if (!year || !month || !day) return null
    return new Date(year, month - 1, day)
}

const addMonths = (date: Date, months: number) => {
    const next = new Date(date.getFullYear(), date.getMonth() + months, date.getDate())
    if (next.getMonth() !== (date.getMonth() + months) % 12) {
        next.setDate(0)
    }
    return next
}

const enforcePrintRange = () => {
    if (!printForm.from) return
    const start = parseYmd(printForm.from)
    if (!start) return
    const maxEnd = addMonths(start, 2)
    maxEnd.setDate(maxEnd.getDate() - 1)
    const maxEndYmd = toYmd(maxEnd.getFullYear(), maxEnd.getMonth(), maxEnd.getDate())

    if (!printForm.to) {
        printForm.to = maxEndYmd
        return
    }

    const end = parseYmd(printForm.to)
    if (!end) {
        printForm.to = maxEndYmd
        return
    }

    if (end < start) {
        printForm.to = printForm.from
        return
    }

    if (end > maxEnd) {
        printForm.to = maxEndYmd
    }
}

watch(() => printForm.from, enforcePrintRange)
watch(() => printForm.to, enforcePrintRange)

const loadEmployees = async () => {
    employees.value = await listEmployees()
}

const loadAbsenceTypes = async () => {
    absenceTypes.value = await listAbsenceTypes()
}

const loadPublicHolidays = async () => {
    publicHolidays.value = await listPublicHolidays('metropole', selectedYear.value)
}

const loadAbsences = async () => {
    const monthStart = toYmd(selectedYear.value, selectedMonth.value, 1)
    const monthEnd = toYmd(selectedYear.value, selectedMonth.value + 1, 0)
    absences.value = await listAbsences({
        from: monthStart,
        to: monthEnd,
        siteIds: selectedSiteIds.value
    })
}

onMounted(async () => {
    await Promise.all([loadEmployees(), loadAbsenceTypes(), loadPublicHolidays(), loadAbsences()])
})

watch([selectedMonth, selectedYear, selectedSiteIds], async () => {
    await loadAbsences()
})

watch(selectedYear, async () => {
    await loadPublicHolidays()
})

// Indexation des absences par cellule pour eviter un find() a chaque case.
const cellAbsenceMap = computed(() => {
    const map = new Map<string, { id: number; code: string; color: string; textColor?: string }>()
    const monthStart = monthStartDate.value
    const monthEnd = monthEndDate.value

    for (const absence of absences.value) {
        const employeeId = absence.employee?.id
        if (!employeeId) continue
        const start = parseYmd(normalizeDate(absence.startDate))
        const end = parseYmd(normalizeDate(absence.endDate))
        if (!start || !end) continue

        const rangeStart = start < monthStart ? monthStart : start
        const rangeEnd = end > monthEnd ? monthEnd : end
        if (rangeEnd < rangeStart) continue

        for (
            let currentDate = new Date(rangeStart.getTime());
            currentDate <= rangeEnd;
            currentDate.setDate(currentDate.getDate() + 1)
        ) {
            const key = `${employeeId}-${toYmd(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate())}`
            map.set(key, {
                id: absence.id,
                code: absence.type?.code ?? '',
                color: absence.type?.color ?? '#222783'
            })
        }
    }

    return map
})

const isHolidayDate = (date: string) => {
    return Boolean(publicHolidays.value[date])
}

const getCellAbsence = (employeeId: number, date: string) => {
    if (isHolidayDate(date)) {
        return {
            id: 0,
            code: 'F',
            color: '#b3e5fc',
            textColor: '#0f172a'
        }
    }
    const absence = cellAbsenceMap.value.get(`${employeeId}-${date}`)
    if (absence) return absence
    return null
}

const getCellStyle = (employeeId: number, date: string) => {
    const absence = getCellAbsence(employeeId, date)
    if (!absence) return undefined

    return {
        backgroundColor: absence.color,
        color: absence.textColor ?? '#fff'
    }
}

const getCellCode = (employeeId: number, date: string) => {
    return getCellAbsence(employeeId, date)?.code ?? ''
}

const openCreate = (employee: Employee, date: string) => {
    if (isHolidayDate(date)) {
        window.alert("Impossible de creer une absence un jour ferie.")
        return
    }

    const existing = absences.value.find((absence) => {
        const start = normalizeDate(absence.startDate)
        const end = normalizeDate(absence.endDate)
        return absence.employee?.id === employee.id && date >= start && date <= end
    })

    if (existing) {
        editingAbsence.value = existing
        form.employeeId = existing.employee.id
        form.typeId = existing.type.id
        form.startDate = normalizeDate(existing.startDate)
        form.endDate = normalizeDate(existing.endDate)
        form.comment = existing.comment ?? ''
    } else {
        editingAbsence.value = null
        form.employeeId = employee.id
        form.startDate = date
        form.endDate = date
        form.typeId = ''
        form.comment = ''
    }

    isDrawerOpen.value = true
}

const openCreateFromToday = () => {
    editingAbsence.value = null
    form.employeeId = ''
    form.typeId = ''
    const now = new Date()
    const today = toYmd(now.getFullYear(), now.getMonth(), now.getDate())
    if (isHolidayDate(today)) {
        window.alert("Impossible de creer une absence un jour ferie.")
        return
    }
    form.startDate = today
    form.endDate = today
    form.comment = ''
    isDrawerOpen.value = true
}

const hasHolidayInRange = (startDate: string, endDate: string) => {
    const start = parseYmd(startDate)
    const end = parseYmd(endDate)
    if (!start || !end) return false
    for (
        let currentDate = new Date(start.getTime());
        currentDate <= end;
        currentDate.setDate(currentDate.getDate() + 1)
    ) {
        const key = toYmd(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate())
        if (isHolidayDate(key)) {
            return true
        }
    }
    return false
}

const handleSubmit = async () => {
    if (isSubmitting.value) return

    isSubmitting.value = true
    try {
        const start = normalizeDate(form.startDate)
        const end = normalizeDate(form.endDate)
        if (hasHolidayInRange(start, end)) {
            window.alert("Impossible de creer une absence sur un jour ferie.")
            return
        }
        const overlaps = absences.value.filter((absence) => {
            if (absence.employee?.id !== Number(form.employeeId)) return false
            if (editingAbsence.value && absence.id === editingAbsence.value.id) return false
            const aStart = normalizeDate(absence.startDate)
            const aEnd = normalizeDate(absence.endDate)
            return start <= aEnd && end >= aStart
        })

        if (overlaps.length > 0) {
            // Securise le chevauchement: on demande confirmation avant suppression.
            const confirmReplace = window.confirm(
                "Cette absence chevauche une autre. Voulez-vous la remplacer ?"
            )
            if (!confirmReplace) return
            for (const overlap of overlaps) {
                await deleteAbsence(overlap.id)
            }
        }

        if (editingAbsence.value) {
            await updateAbsence({
                id: editingAbsence.value.id,
                employeeId: Number(form.employeeId),
                typeId: Number(form.typeId),
                startDate: form.startDate,
                endDate: form.endDate,
                comment: form.comment
            })
        } else {
            await createAbsence({
                employeeId: Number(form.employeeId),
                typeId: Number(form.typeId),
                startDate: form.startDate,
                endDate: form.endDate,
                comment: form.comment
            })
        }

        closeDrawer()
        await loadAbsences()
    } finally {
        isSubmitting.value = false
    }
}

const handleDelete = async () => {
    if (!editingAbsence.value) return

    const confirmDelete = window.confirm('Supprimer cette absence ?')
    if (!confirmDelete) return

    await deleteAbsence(editingAbsence.value.id)
    closeDrawer()
    await loadAbsences()
}

const formatEmployeeName = (employee: Employee) => {
    const initial = employee.lastName ? `${employee.lastName[0].toUpperCase()}.` : ''
    return `${employee.firstName} ${initial}`.trim()
}

const { printPdf } = usePdfPrinter()
const handlePrint = async () => {
    const params = new URLSearchParams()
    params.set('from', printForm.from)
    params.set('to', printForm.to)
    if (printForm.siteIds.length > 0) {
        params.set('sites', printForm.siteIds.join(','))
    }
    await printPdf(`/absences/print?${params.toString()}`)
    isPrintOpen.value = false
}
</script>
