<script lang="ts">
	import { cn } from '../../helper/cls.js';
	import type { ClassValue } from 'tailwind-variants';

	/**
	 * Pagination component props
	 */
	export interface PaginationProps {
		/** Current page number (1-indexed). If not provided, component manages state internally. */
		currentPage?: number;
		/** Total number of items across all pages */
		totalItems: number;
		/** Number of items per page */
		pageSize?: number;
		/** Callback when page changes */
		onPageChange?: (page: number) => void;
		/** Callback when page size changes */
		onPageSizeChange?: (pageSize: number) => void;
		/** Callback when first page is clicked */
		onFirstPage?: () => void;
		/** Callback when last page is clicked */
		onLastPage?: () => void;
		/** Whether pagination controls are disabled */
		disabled?: boolean;
		/** Show page size selector */
		showPageSize?: boolean;
		/** Available page size options */
		pageSizeOptions?: number[];
		/** Pagination template mode: 'full' shows page numbers, 'compact' shows "Page X of Y" */
		template?: 'full' | 'compact';
		/** Show "Showing X to Y of Z results" text */
		showInfo?: boolean;
		/** Show navigation buttons (first, prev, next, last) */
		showNavigation?: boolean;
		/** Show first/last page buttons */
		showFirstLast?: boolean;
		/** Show page number buttons (only applies when template='full') */
		showPageNumbers?: boolean;
		/** Maximum number of visible page buttons */
		maxVisiblePages?: number;
		/** Always show first and last page numbers when there are many pages */
		alwaysShowFirstLast?: boolean;
		/** Hide pagination when there's only one page */
		hideWhenSinglePage?: boolean;
		/** Hide pagination when there are no items */
		hideWhenNoItems?: boolean;
		/** Custom class for wrapper */
		class?: ClassValue;
		/** Custom class for info text */
		infoClass?: ClassValue;
		/** Custom class for navigation buttons */
		buttonClass?: ClassValue;
		/** Custom class for active page button */
		activeButtonClass?: ClassValue;
		/** Use internal state management (when currentPage is not provided) */
		internalState?: boolean;
	}

	let {
		currentPage: externalCurrentPage,
		totalItems,
		pageSize: externalPageSize,
		onPageChange,
		onPageSizeChange,
		onFirstPage,
		onLastPage,
		disabled = false,
		showPageSize = false,
		pageSizeOptions = [5, 10, 25, 50, 100],
		template = 'full',
		showInfo = true,
		showNavigation = true,
		showFirstLast = true,
		showPageNumbers = true,
		maxVisiblePages = 5,
		alwaysShowFirstLast = true,
		hideWhenSinglePage = true,
		hideWhenNoItems = true,
		class: wrapperClass = '',
		infoClass = '',
		buttonClass = '',
		activeButtonClass = '',
		internalState = false
	}: PaginationProps = $props();

	// Internal state management
	const defaultPageSize = 10;
	let internalCurrentPage = $state(externalCurrentPage ?? 1);
	let internalPageSize = $state(externalPageSize ?? defaultPageSize);

	// Use external values if provided, otherwise use internal state or defaults
	const currentPage = $derived(
		externalCurrentPage !== undefined
			? externalCurrentPage
			: internalState
				? internalCurrentPage
				: 1
	);
	const pageSize = $derived(
		externalPageSize !== undefined
			? externalPageSize
			: internalState
				? internalPageSize
				: defaultPageSize
	);

	// Sync external page size changes
	$effect(() => {
		if (externalPageSize !== undefined) {
			internalPageSize = externalPageSize;
		}
	});

	// Sync external current page changes
	$effect(() => {
		if (externalCurrentPage !== undefined) {
			internalCurrentPage = externalCurrentPage;
		}
	});

	// Validation: ensure pageSize is valid
	const validPageSize = $derived(Math.max(1, pageSize));
	// Validation: ensure totalItems is non-negative
	const validTotalItems = $derived(Math.max(0, totalItems));

	// Computed values
	const totalPages = $derived(Math.ceil(validTotalItems / validPageSize));
	const startItem = $derived(Math.min((currentPage - 1) * validPageSize + 1, validTotalItems));
	const endItem = $derived(Math.min(currentPage * validPageSize, validTotalItems));

	// Ensure currentPage is within valid range
	const validCurrentPage = $derived(Math.max(1, Math.min(currentPage, totalPages || 1)));

	// Navigation functions
	function goToPage(page: number) {
		if (!disabled && page >= 1 && page <= totalPages && page !== validCurrentPage) {
			if (internalState && externalCurrentPage === undefined) {
				internalCurrentPage = page;
			}
			onPageChange?.(page);
		}
	}

	function firstPage() {
		if (!disabled && validCurrentPage > 1) {
			const targetPage = 1;
			if (internalState && externalCurrentPage === undefined) {
				internalCurrentPage = targetPage;
			}
			onPageChange?.(targetPage);
			onFirstPage?.();
		}
	}

	function lastPage() {
		if (!disabled && validCurrentPage < totalPages) {
			const targetPage = totalPages;
			if (internalState && externalCurrentPage === undefined) {
				internalCurrentPage = targetPage;
			}
			onPageChange?.(targetPage);
			onLastPage?.();
		}
	}

	function nextPage() {
		if (!disabled && validCurrentPage < totalPages) {
			const targetPage = validCurrentPage + 1;
			if (internalState && externalCurrentPage === undefined) {
				internalCurrentPage = targetPage;
			}
			onPageChange?.(targetPage);
		}
	}

	function prevPage() {
		if (!disabled && validCurrentPage > 1) {
			const targetPage = validCurrentPage - 1;
			if (internalState && externalCurrentPage === undefined) {
				internalCurrentPage = targetPage;
			}
			onPageChange?.(targetPage);
		}
	}

	function handlePageSizeChange(event: Event) {
		if (disabled) return;
		const select = event.target as HTMLSelectElement;
		const newPageSize = parseInt(select.value, 10);

		if (isNaN(newPageSize) || newPageSize < 1) return;

		// Calculate new total pages
		const newTotalPages = Math.ceil(validTotalItems / newPageSize);

		// Adjust current page if it would exceed the new total pages
		let adjustedPage = validCurrentPage;
		if (validCurrentPage > newTotalPages) {
			adjustedPage = newTotalPages || 1;
			if (internalState && externalCurrentPage === undefined) {
				internalCurrentPage = adjustedPage;
			}
			onPageChange?.(adjustedPage);
		}

		// Update page size
		if (internalState && externalPageSize === undefined) {
			internalPageSize = newPageSize;
		}
		onPageSizeChange?.(newPageSize);
	}

	/**
	 * Calculate which page numbers to display
	 */
	function getPageNumbers(): number[] {
		if (totalPages <= maxVisiblePages) {
			// Show all pages if total is less than or equal to max
			return Array.from({ length: totalPages }, (_, i) => i + 1);
		}

		const pages: number[] = [];
		const halfMax = Math.floor(maxVisiblePages / 2);

		// Calculate start and end of visible range, centered around current page
		let start = Math.max(1, validCurrentPage - halfMax);
		let end = Math.min(totalPages, start + maxVisiblePages - 1);

		// Adjust if we're near the end
		if (end - start + 1 < maxVisiblePages) {
			start = Math.max(1, end - maxVisiblePages + 1);
		}

		// Build page numbers array
		for (let i = start; i <= end; i++) {
			pages.push(i);
		}

		return pages;
	}

	// Determine if we should show pagination
	const shouldShowPagination = $derived(!hideWhenNoItems || validTotalItems > 0);
	const shouldShowControls = $derived(!hideWhenSinglePage || totalPages > 1);

	// Default classes using ripple-ui design system
	const defaultWrapperClass = 'flex items-center justify-between p-4';
	const defaultInfoClass = 'text-default-500 text-sm';
	const defaultButtonClass =
		'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium';
	const defaultActiveButtonClass = 'bg-primary-100 text-primary-700';
	const defaultInactiveButtonClass = 'text-default-700 hover:bg-default-100';
	const defaultDisabledButtonClass = 'text-default-300 cursor-not-allowed';
	const defaultPageButtonClass =
		'relative inline-flex items-center rounded-md px-3 py-1 text-sm font-medium';
</script>

{#if shouldShowPagination}
	<div class={cn(defaultWrapperClass, wrapperClass)}>
		{#if showInfo}
			<div class="flex items-center gap-2">
				{#if showPageSize}
					<div class="flex items-center gap-2">
						<label for="pagination-page-size" class="text-default-500 text-sm">Show</label>
						<select
							id="pagination-page-size"
							class={cn(
								'border-default-200 rounded-md border px-2 py-1 text-sm',
								disabled && 'cursor-not-allowed opacity-50'
							)}
							value={pageSize}
							onchange={handlePageSizeChange}
							{disabled}
						>
							{#each pageSizeOptions as option (option)}
								<option value={option}>{option}</option>
							{/each}
						</select>
						<span class="text-default-500 text-sm">entries</span>
					</div>
				{/if}
				<span class={cn(defaultInfoClass, infoClass)}>
					Showing {startItem} to {endItem} of {validTotalItems} entries
				</span>
			</div>
		{/if}

		{#if shouldShowControls && showNavigation}
			<div class="flex items-center gap-1">
				{#if showFirstLast}
					<button
						onclick={firstPage}
						disabled={disabled || validCurrentPage === 1}
						class={cn(
							defaultButtonClass,
							buttonClass,
							disabled || validCurrentPage === 1
								? defaultDisabledButtonClass
								: defaultInactiveButtonClass
						)}
						aria-label="First page"
						type="button"
					>
						«
					</button>
				{/if}

				<button
					onclick={prevPage}
					disabled={disabled || validCurrentPage === 1}
					class={cn(
						defaultButtonClass,
						buttonClass,
						disabled || validCurrentPage === 1
							? defaultDisabledButtonClass
							: defaultInactiveButtonClass
					)}
					aria-label="Previous page"
					type="button"
				>
					‹
				</button>

				{#if template === 'full' && showPageNumbers}
					{#if totalPages <= maxVisiblePages}
						<!-- Show all pages if total is less than or equal to maxVisiblePages -->
						{#each Array(totalPages) as page, i (page + '-' + i)}
							{@const pageNum = i + 1}
							<button
								onclick={() => goToPage(pageNum)}
								{disabled}
								class={cn(
									defaultPageButtonClass,
									buttonClass,
									validCurrentPage === pageNum
										? cn(defaultActiveButtonClass, activeButtonClass)
										: defaultInactiveButtonClass
								)}
								aria-label="Page {pageNum}"
								aria-current={validCurrentPage === pageNum ? 'page' : undefined}
								type="button"
							>
								{pageNum}
							</button>
						{/each}
					{:else}
						<!-- Smart pagination for many pages -->
						{@const pageNumbers = getPageNumbers()}
						{@const showFirst = alwaysShowFirstLast && pageNumbers[0] > 1}
						{@const showLast =
							alwaysShowFirstLast && pageNumbers[pageNumbers.length - 1] < totalPages}

						{#if showFirst}
							<button
								onclick={() => goToPage(1)}
								{disabled}
								class={cn(
									defaultPageButtonClass,
									buttonClass,
									validCurrentPage === 1
										? cn(defaultActiveButtonClass, activeButtonClass)
										: defaultInactiveButtonClass
								)}
								aria-label="Page 1"
								aria-current={validCurrentPage === 1 ? 'page' : undefined}
								type="button"
							>
								1
							</button>
							{#if pageNumbers[0] > 2}
								<span class="text-default-400 px-2 text-sm">...</span>
							{/if}
						{/if}

						{#each pageNumbers as pageNum (pageNum)}
							<button
								onclick={() => goToPage(pageNum)}
								{disabled}
								class={cn(
									defaultPageButtonClass,
									buttonClass,
									validCurrentPage === pageNum
										? cn(defaultActiveButtonClass, activeButtonClass)
										: defaultInactiveButtonClass
								)}
								aria-label="Page {pageNum}"
								aria-current={validCurrentPage === pageNum ? 'page' : undefined}
								type="button"
							>
								{pageNum}
							</button>
						{/each}

						{#if showLast}
							{#if pageNumbers[pageNumbers.length - 1] < totalPages - 1}
								<span class="text-default-400 px-2 text-sm">...</span>
							{/if}
							<button
								onclick={() => goToPage(totalPages)}
								{disabled}
								class={cn(
									defaultPageButtonClass,
									buttonClass,
									validCurrentPage === totalPages
										? cn(defaultActiveButtonClass, activeButtonClass)
										: defaultInactiveButtonClass
								)}
								aria-label="Page {totalPages}"
								aria-current={validCurrentPage === totalPages ? 'page' : undefined}
								type="button"
							>
								{totalPages}
							</button>
						{/if}
					{/if}
				{:else if template === 'compact'}
					<span class="text-default-500 px-2 text-sm">
						Page {validCurrentPage} of {totalPages}
					</span>
				{/if}

				<button
					onclick={nextPage}
					disabled={disabled || validCurrentPage === totalPages}
					class={cn(
						defaultButtonClass,
						buttonClass,
						disabled || validCurrentPage === totalPages
							? defaultDisabledButtonClass
							: defaultInactiveButtonClass
					)}
					aria-label="Next page"
					type="button"
				>
					›
				</button>

				{#if showFirstLast}
					<button
						onclick={lastPage}
						disabled={disabled || validCurrentPage === totalPages}
						class={cn(
							defaultButtonClass,
							buttonClass,
							disabled || validCurrentPage === totalPages
								? defaultDisabledButtonClass
								: defaultInactiveButtonClass
						)}
						aria-label="Last page"
						type="button"
					>
						»
					</button>
				{/if}
			</div>
		{/if}
	</div>
{/if}
