<script setup lang="ts">
	import { computed, nextTick, onMounted, ref, inject, watch, type Ref, onUnmounted } from 'vue'
	import type { VDataTable, VDataTableServer } from 'vuetify/components/VDataTable'
	import { locales } from './locales'
	import SyIcon from '@/components/Customs/SyIcon/SyIcon.vue'

	type HeaderPropsRaw = {
		class?: string | string[]
		style?: Record<string, string | number>
	}

	const props = withDefaults(defineProps<{
		column: Parameters<VDataTable['$slots']['headers']>['0']['columns'][number]
		headerParams: Parameters<VDataTable['$slots']['headers']>['0']
		table: VDataTable | VDataTableServer | null | undefined
		resizableColumns?: boolean
		wrapTitle?: boolean
		storageKey?: string
		headerPropsRaw?: HeaderPropsRaw
	}>(), {
		resizableColumns: false,
		wrapTitle: false,
		storageKey: undefined,
		headerPropsRaw: undefined,
	})

	// Derive alignment from header definition and headerProps.class (Vuetify-like)
	function getAlignmentClass(align?: string, rawClass?: string | string[]): string {
		if (align === 'center') return 'justify-center'
		if (align === 'end') return 'justify-end'

		if (rawClass) {
			const classes = Array.isArray(rawClass)
				? rawClass
				: String(rawClass).split(/\s+/)

			if (classes.includes('text-center')) return 'justify-center'
			if (classes.some(c => ['text-end', 'text-right'].includes(c))) {
				return 'justify-end'
			}
		}

		return ''
	}

	const alignClass = computed(() =>
		getAlignmentClass(header.value?.align, props.headerPropsRaw?.class),
	)

	// Extract inline style passed via headerProps.style
	const headerStyle = computed<Record<string, string | number> | undefined>(() => props.headerPropsRaw?.style)
	const headerClassRaw = computed<string | string[] | undefined>(() => props.headerPropsRaw?.class)

	// Derive text alignment utility class for the title content (Vuetify-like)
	const textAlignClass = computed((): string => {
		const a = header.value?.align
		if (a === 'center') return 'text-center'
		if (a === 'end') return 'text-end'

		const cls = props.headerPropsRaw?.class
		if (cls !== undefined) {
			const classes = Array.isArray(cls) ? cls : String(cls).split(/\s+/)
			if (classes.includes('text-center')) return 'text-center'
			if (classes.includes('text-end')) return 'text-end'
			if (classes.includes('text-right')) return 'text-end'
		}
		return ''
	})

	const header = computed(() => {
		return props.headerParams.columns.find(header => header.key === props.column.key)
	})

	const isLastColumn = computed(() => {
		const columnIndex = props.headerParams.columns.findIndex(header => header.key === props.column.key)
		return columnIndex === props.headerParams.columns.length - 1
	})

	const wrapper = ref<HTMLElement | null>(null)

	const initialWidth = ref<number | null>(null)

	// Inject stored column widths if available
	const storedColumnWidths = inject<Ref<Record<string, number | string>>>('columnWidths', ref({}))

	onMounted(() => {
		nextTick(() => {
			if (wrapper.value) {
				initialWidth.value = wrapper.value.offsetWidth

				// Apply stored width if available for this column
				if (header.value?.key && storedColumnWidths?.value?.[header.value.key]) {
					header.value.width = storedColumnWidths.value[header.value.key]
				}
			}
		})
	})

	// Re-apply column width on header changes (ex when table re-renders due to pagination)
	watch(
		() => props.column,
		() => {
			nextTick(() => {
				if (header.value?.key && storedColumnWidths?.value?.[header.value.key]) {
					header.value.width = storedColumnWidths.value[header.value.key]
				}
			})
		},
		{ immediate: true },
	)

	// Watch for changes to column widths and apply them
	watch(
		() => storedColumnWidths?.value,
		(newWidths) => {
			if (newWidths && header.value?.key && newWidths[header.value.key]) {
				header.value.width = newWidths[header.value.key]
			}
		},
		{ deep: true },
	)

	const updateColumnWidth = inject<(key: string, width: number | string) => void>('updateColumnWidth', () => {})

	function resetColumnWidth() {
		if (header.value && initialWidth.value) {
			header.value.width = initialWidth.value

			// Save the reset width to localStorage if column key exists
			if (header.value.key) {
				updateColumnWidth(header.value.key, initialWidth.value)
			}
		}
	}

	function startResize(event: MouseEvent) {
		const startX = event.clientX
		const startWidth = wrapper.value!.offsetWidth + 24 + 1 / 2 * 16

		function onMouseMove(e: MouseEvent) {
			const newWidth = Math.max(50, startWidth + (e.clientX - startX))
			if (wrapper.value) {
				header.value!.width = newWidth
			}
		}

		function onMouseUp() {
			document.removeEventListener('mousemove', onMouseMove)
			document.removeEventListener('mouseup', onMouseUp)

			// Save column width to localStorage if column key exists
			if (header.value?.key) {
				updateColumnWidth(header.value.key, header.value.width || initialWidth.value || 0)
			}
		}

		document.addEventListener('mousemove', onMouseMove)
		document.addEventListener('mouseup', onMouseUp)
	}

	const tableWidth = ref(0)
	function updateTableWidth() {
		if (props.table) {
			tableWidth.value = props.table.$el.offsetWidth
		}
	}
	onMounted(async () => {
		await nextTick()
		updateTableWidth()
	})
	window.addEventListener('resize', updateTableWidth)

	onUnmounted(() => {
		window.removeEventListener('resize', updateTableWidth)
	})

	function resizeKeyboardColumn(increment: number) {
		// Même base de largeur que le redimensionnement souris (`startResize`) : `wrapper` est
		// le contenu de l'en-tête, ~32px plus étroit que la colonne réelle. Sans cette
		// correction, le 1er appui rétrécissait la colonne d'un coup (« se serre à gauche »).
		const currentWidth = (wrapper.value?.offsetWidth || 0) + 24 + 1 / 2 * 16
		if (wrapper.value) {
			const newWidth = Math.max(50, currentWidth + increment)
			header.value!.width = newWidth

			// Save column width to localStorage if column key exists
			if (header.value?.key) {
				updateColumnWidth(header.value.key, newWidth)
			}
		}
	}

	const isColumnSorted = computed(() => {
		return props.headerParams.sortBy.some((sort) => {
			return sort.key === props.column.key
		})
	})

	// Get the sort order index (for multi-sort)
	const sortOrderIndex = computed(() => {
		if (!props.headerParams.sortBy || props.headerParams.sortBy.length <= 1) return null

		const index = props.headerParams.sortBy.findIndex(sort => sort.key === props.column.key)
		return index !== -1 ? index + 1 : null
	})

	const titleClasses = computed(() => [
		textAlignClass.value,
		headerClassRaw.value,
		{ 'col-title--wrap': props.wrapTitle },
	])

	const wrapperClasses = computed(() => [
		alignClass.value,
		{
			'h-100': !props.wrapTitle || props.resizableColumns,
			'v-data-table-header__content--wrap': props.wrapTitle && !props.resizableColumns,
		},
	])
</script>

<template>
	<div
		ref="wrapper"
		class="v-data-table-header__content d-flex align-center w-100"
		:class="wrapperClasses"
	>
		<slot
			:name="`header.${column.key}`"
			v-bind="{
				column,
				locales,
				...headerParams,
			}"
		>
			<div
				class="col-title"
				:class="titleClasses"
				:style="headerStyle"
			>
				{{ column.title }}
			</div>
		</slot>
		<div
			v-if="header!.sortable"
			class="sort-container d-flex align-center"
		>
			<button
				type="button"
				class="sort-button"
				:class="{ 'sort-button--active': isColumnSorted }"
				:title="locales.columnOrder(column.title!)"
				:aria-label="locales.columnOrder(column.title!)"
				@click="headerParams.toggleSort(column)"
			>
				<SyIcon
					class="v-data-table-header__sort-icon"
					:class="{ 'text-primary opacity-100' : isColumnSorted }"
					:icon="headerParams.getSortIcon(column)"
					decorative
				/>
			</button>
			<div
				v-if="sortOrderIndex"
				class="sort-order-indicator text-primary ml-0 mr-2"
				:title="locales.sortOrder(sortOrderIndex)"
			>
				{{ sortOrderIndex }}
			</div>
			<div
				v-else-if="isColumnSorted"
				class="mr-2"
			/>
		</div>
		<button
			v-if="props.resizableColumns && !isLastColumn"
			type="button"
			class="resizer"
			tabindex="0"
			role="separator"
			:aria-valuenow="(column.width as number)"
			:aria-valuemin="0"
			:aria-valuemax="tableWidth"
			:aria-label="locales.ResizableColumn"
			@mousedown="startResize"
			@dblclick="resetColumnWidth"
			@keydown.right.prevent="resizeKeyboardColumn(10)"
			@keydown.left.prevent="resizeKeyboardColumn(-10)"
		/>
	</div>
</template>

<style lang="scss" scoped>
.v-data-table-header__content {
	column-gap: 4px;
	min-width: 0;
}

.v-data-table-header__content--wrap {
	height: auto;
}

.col-title {
	flex: 1 1 auto;
	min-width: 0;
}

.col-title--wrap {
	white-space: normal;
	overflow-wrap: anywhere;
	word-break: break-word;
	line-height: 1.25;
}

.sort-container {
	flex: 0 0 auto;
}

// Bouton de tri natif : focusable et actionnable au clavier (Entrée/Espace).
.sort-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 2px;
	margin: 0;
	border: 0;
	border-radius: 4px;
	background: transparent;
	color: inherit;
	cursor: pointer;
}

// L'icône de tri est masquée par défaut (opacity 0 via Vuetify) : on la révèle
// au survol ET au focus clavier, sinon le tri est invisible en navigation clavier.
.sort-button:hover :deep(.v-data-table-header__sort-icon),
.sort-button:focus-visible :deep(.v-data-table-header__sort-icon),
.sort-button--active :deep(.v-data-table-header__sort-icon) {
	opacity: 1;
}

// Focus visible fortement contrasté.
.sort-button:focus-visible {
	outline: 2px solid rgb(var(--v-theme-primary));
	outline-offset: 2px;
}

.sort-order-indicator {
	font-size: 0.75rem;
	font-weight: bold;
	line-height: 1;
}

.resizer {
	margin-left: auto;
	flex: 0 0 auto;
	cursor: col-resize;
	background-color: #f0f0f0;
	position: relative;
	width: 1rem;
	height: 100%;
	left: calc(24px - 1rem / 2);

	// Poignée de redimensionnement opérable au clavier (`tabindex="0"`). La poignée dépasse le
	// bord de la cellule (`left`) : un anneau (outline/box-shadow) déborde forcément, et un
	// fond aussi. Le seul indicateur contenu est son trait central (`::after`) : au focus, il
	// passe en primary et s'élargit. Pas d'anneau, pas de fond.
	&:focus-visible {
		// Neutralise le ring global `button:focus-visible` de `_btns.scss` (outline-offset: 3px),
		// qui déborde sur cette poignée fine et décalée. `!important` pour être sûr de gagner
		// quel que soit l'ordre d'injection. L'indicateur de focus est le trait central ci-dessous.
		outline: none !important;

		&::after {
			top: 50%;
			height: 60%;
			width: 0.2rem;
			transform: translate(-50%, -50%);
			background: rgb(var(--v-theme-primary));
		}
	}

	&::after {
		content: '';
		position: absolute;
		top: 0;
		left: 50%;
		transform: translateX(-50%);
		width: 0.1rem;
		height: 100%;
		background:
			repeating-linear-gradient(
				transparent,
				#a7a7a7 0,
				#a7a7a7 5px,
				transparent 5px,
				transparent 7px
			);
	}
}
</style>
