import type { ComputedRef, Ref } from 'vue'
import type { TableSelectionOptions } from '../../types/TableSchema'
import { computed, ref } from 'vue'

export interface UseTableSelectionOptions<T> extends TableSelectionOptions<T> {
	cleanData?: (item: T) => T
	data: Ref<T[]> | ComputedRef<T[]>
}

export function useTableSelection<T extends { [key: string]: any }>(options: UseTableSelectionOptions<T>) {
	const allSelectorEl = ref<HTMLInputElement>()
	const lastSelectedIndex = ref<number | null>(null)
	const computedSelectedItems = computed(() => options.selectedItems.value)
	const isSelectable = computed(() => options.selectable === true && Array.isArray(options.selectedItems.value))
	const allItems = computed(() => options.data.value)

	function updateAllSelectorState() {
		if (allSelectorEl.value === undefined) { return }

		// If we don't know the total items, we can't determine if "all" are selected
		if (!allItems.value) {
			allSelectorEl.value.indeterminate = options.selectedItems.value.length > 0
			return
		}

		// Get all possible item IDs
		const allItemIds = allItems.value.map(item => item.id)

		// Check if all items are selected
		const allSelected
    = allItemIds.length > 0
    	&& options.selectedItems.value.length === allItemIds.length
    	&& allItemIds.every(id => options.selectedItems.value.includes(id))

		// Set checked state
		allSelectorEl.value.checked = allSelected

		// Set indeterminate state (some but not all items selected)
		allSelectorEl.value.indeterminate
    = !allSelected && options.selectedItems.value.length > 0
	}

	function toggleSelectItem(item: T, event?: MouseEvent) {
		const currentIndex = allItems.value.findIndex(i => i.id === item.id)

		// Handle Shift+click for range selection
		if (event?.shiftKey && lastSelectedIndex.value !== null && currentIndex !== -1) {
			const startIndex = Math.min(lastSelectedIndex.value, currentIndex)
			const endIndex = Math.max(lastSelectedIndex.value, currentIndex)

			// Get all items in the range
			const rangeItems = allItems.value.slice(startIndex, endIndex + 1)
			const rangeItemIds = rangeItems.map(i => i.id)

			// Check if all items in range are currently selected
			const allRangeSelected = rangeItemIds.every(id => options.selectedItems.value.includes(id)
			)

			if (allRangeSelected) {
				// Deselect all items in range
				options.selectedItems.value = options.selectedItems.value.filter(id => !rangeItemIds.includes(id)
				)
			} else {
				// Select all items in range (add missing ones)
				const newSelections = rangeItemIds.filter(id => !options.selectedItems.value.includes(id)
				)
				options.selectedItems.value.push(...newSelections)
			}

			return
		}

		// Regular single item selection logic
		if (computedSelectedItems.value.length === 0) {
			// Clean the item if a cleanData function is provided, otherwise use the default cleaning
			const cleanItem = options.cleanData ? options.cleanData(item) : { ...item }

			// If no cleanData function is provided, use the default cleaning
			if (!options.cleanData) {
				Object.keys(cleanItem).forEach((key) => {
					if (key.startsWith('_')) {
						delete cleanItem[key]
					}
				})
			}

			options.onSelect(cleanItem)
			lastSelectedIndex.value = currentIndex
			return
		}

		const index = computedSelectedItems.value.indexOf(item.id)
		if (index > -1) {
			options.selectedItems.value.splice(index, 1)
		} else {
			options.selectedItems.value.push(item.id)
		}

		// Update last selected index for future range selections
		lastSelectedIndex.value = currentIndex
	}

	// Update toggleSelectAll to pass allItems to updateAllSelectorState
	function toggleSelectAll(event: Event) {
		const value = (event.target as HTMLInputElement).checked

		if (allItems.value && value) {
			options.selectedItems.value = allItems.value.map(item => item.id)
		} else {
			options.selectedItems.value = []
		}

		// Reset last selected index when selecting/deselecting all
		lastSelectedIndex.value = null
	}

	return {
		computedSelectedItems,
		isSelectable,
		allSelectorEl,
		updateAllSelectorState,
		toggleSelectItem,
		toggleSelectAll
	}
}
