<script lang="ts" generics="T">
	/* eslint-disable no-undef */
	import { ArrowUp, ArrowDown } from 'lucide-svelte';

	interface Column<T> {
		key: keyof T;
		label: string;
		sortable?: boolean;
		format?: (value: T[keyof T], row: T) => string;
		render?: (value: T[keyof T], row: T) => unknown;
		align?: 'left' | 'center' | 'right';
		class?: string;
	}

	interface Props {
		data: T[];
		columns: Column<T>[];
		class?: string;
		striped?: boolean;
		hoverable?: boolean;
		compact?: boolean;
	}

	let {
		data,
		columns,
		class: className = '',
		striped = false,
		hoverable = true,
		compact = false
	}: Props = $props();

	let sortKey = $state<keyof T | null>(null);
	let sortDirection = $state<'asc' | 'desc'>('asc');

	const handleSort = (column: Column<T>) => {
		if (!column.sortable) return;

		if (sortKey === column.key) {
			sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
		} else {
			sortKey = column.key;
			sortDirection = 'asc';
		}
	};

	const sortedData = $derived(
		!sortKey
			? data
			: [...data].sort((a, b) => {
					const aVal = a[sortKey as keyof T];
					const bVal = b[sortKey as keyof T];

					if (aVal === bVal) return 0;

					const comparison = aVal < bVal ? -1 : 1;
					return sortDirection === 'asc' ? comparison : -comparison;
				})
	);

	const getValue = (row: T, column: Column<T>) => {
		const value = row[column.key];
		if (column.format) {
			return column.format(value, row);
		}
		return value;
	};
</script>

<div class="overflow-x-auto {className}">
	<table class="table" class:table-zebra={striped} class:table-xs={compact}>
		<thead>
			<tr>
				{#each columns as column}
					<th
						class:cursor-pointer={column.sortable}
						class:hover:bg-base-200={column.sortable}
						class="text-{column.align || 'left'} {column.class || ''}"
						onclick={() => handleSort(column)}
					>
						<div
							class="flex items-center gap-2"
							class:justify-end={column.align === 'right'}
							class:justify-center={column.align === 'center'}
						>
							<span>{column.label}</span>
							{#if column.sortable}
								<div class="flex flex-col opacity-40">
									<ArrowUp
										class="h-3 w-3 {sortKey === column.key && sortDirection === 'asc'
											? 'text-primary opacity-100'
											: ''}"
									/>
									<ArrowDown
										class="-mt-1 h-3 w-3 {sortKey === column.key && sortDirection === 'desc'
											? 'text-primary opacity-100'
											: ''}"
									/>
								</div>
							{/if}
						</div>
					</th>
				{/each}
			</tr>
		</thead>
		<tbody>
			{#each sortedData as row}
				<tr class:hover={hoverable}>
					{#each columns as column}
						<td class="text-{column.align || 'left'} {column.class || ''}">
							{#if column.render}
								{@render column.render(row[column.key], row)}
							{:else}
								{getValue(row, column)}
							{/if}
						</td>
					{/each}
				</tr>
			{/each}
		</tbody>
	</table>
</div>
