<!--
  DatabaseGridWithProvider component using SVAR DataGrid with RestDataProvider
  Provides client-side pagination and data management with REST API integration
-->
<svelte:options runes={true} />

<script lang="ts">
	import { Grid } from 'wx-svelte-grid'
	import type { IColumn, IRow } from 'wx-svelte-grid'
	import { DatabaseProvider } from '$lib/providers/DatabaseProvider'
	import CollectionFilters from '$lib/components/collection/CollectionFilters.svelte'
	import type { CollectionFilterState } from '$lib/components/collection/CollectionFilters.svelte'
	import { onMount, onDestroy } from 'svelte'
	import { goto } from '$app/navigation'
	import { page } from '$app/stores'
	import { createQuery, queryOptions } from '@tanstack/svelte-query'
	import { entityAdapter } from '$lib/api/adapters/entity.adapter'
	import {
		parseFiltersFromUrl,
		buildUrlFromFilters,
		type ParsedFilters,
		ELEMENT_TO_PARAM
	} from '$lib/utils/filterParams'
	import Button from '$lib/components/ui/Button.svelte'
	import Icon from '$lib/components/Icon.svelte'
	import { storeListUrl } from '$lib/utils/listNavigation'

	import type { Snippet } from 'svelte'

	interface Props {
		resource: 'weapons' | 'characters' | 'summons'
		columns: IColumn[]
		pageSize?: number
		leftActions?: Snippet
		headerActions?: Snippet
	}

	const {
		resource,
		columns,
		pageSize: initialPageSize = 20,
		leftActions,
		headerActions
	}: Props = $props()

	// Derive entity type from resource
	const entityType = $derived(
		resource === 'characters' ? 'character' : resource === 'summons' ? 'summon' : 'weapon'
	)

	// Fetch weapon series list for URL slug mapping (only for weapons)
	const weaponSeriesQuery = createQuery(() =>
		queryOptions({
			queryKey: ['weaponSeries', 'list'] as const,
			queryFn: () => entityAdapter.getWeaponSeriesList(),
			enabled: resource === 'weapons',
			staleTime: 1000 * 60 * 60, // 1 hour
			gcTime: 1000 * 60 * 60 * 24 // 24 hours
		})
	)

	// State
	let data = $state<any[]>([])
	let loading = $state(true)
	let currentPage = $state(1)
	let totalPages = $state(1)
	let total = $state(0)
	let searchTerm = $state('')
	let lastSearchTerm = $state('')
	let pageSize = $state(initialPageSize)
	let searchTimeout: ReturnType<typeof setTimeout> | undefined

	// Sort state - tracks which column is sorted and in which direction
	let sortMarks = $state<Record<string, { order: 'asc' | 'desc' }>>({})

	// Filter state
	let elementFilters = $state<number[]>([])
	let rarityFilters = $state<number[]>([])
	let seriesFilters = $state<(number | string)[]>([])
	let proficiencyFilters = $state<number[]>([])
	let seasonFilters = $state<number[]>([])

	// Filter visibility state
	let showFilters = $state(false)

	// Check if any filters are active (for button indicator)
	const hasActiveFilters = $derived(
		elementFilters.length > 0 ||
			rarityFilters.length > 0 ||
			seriesFilters.length > 0 ||
			proficiencyFilters.length > 0 ||
			seasonFilters.length > 0
	)

	// Get selected element name for button styling (only when exactly one element is selected)
	const selectedElement = $derived.by(() => {
		if (elementFilters.length === 1) {
			const elemId = elementFilters[0]
			if (elemId !== undefined) {
				const elemName = ELEMENT_TO_PARAM[elemId]
				if (elemName && elemName !== 'null') {
					return elemName as 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
				}
			}
		}
		return undefined
	})

	// Handle filter changes from CollectionFilters component
	function handleFiltersChange(filters: CollectionFilterState) {
		// Convert series to string[] (weapon series are UUIDs, character series are numbers that need conversion)
		const seriesAsStrings =
			filters.series.length > 0 ? filters.series.map((s) => String(s)) : undefined

		provider.setFilters({
			element: filters.element.length > 0 ? filters.element : undefined,
			rarity: filters.rarity.length > 0 ? filters.rarity : undefined,
			series: seriesAsStrings,
			proficiency1: filters.proficiency.length > 0 ? filters.proficiency : undefined,
			season: filters.season.length > 0 ? filters.season : undefined,
			// For characters, also pass series as characterSeries (they use number enum values)
			characterSeries:
				resource === 'characters' && filters.series.length > 0
					? filters.series.filter((s): s is number => typeof s === 'number')
					: undefined
		})
		loadData(1) // Reset to first page when filters change (this will update URL)
	}

	// Create provider
	const provider = new DatabaseProvider({ resource, pageSize: initialPageSize })

	// Grid API reference
	let api: any

	// Build current filter state for URL building
	function getCurrentFilterState(): CollectionFilterState {
		return {
			element: elementFilters,
			rarity: rarityFilters,
			proficiency: proficiencyFilters,
			season: seasonFilters,
			series: seriesFilters,
			race: [],
			gender: []
		}
	}

	// Update URL with current filters, search, and page (without triggering navigation)
	function updateUrl(pageNum: number) {
		const params = buildUrlFromFilters(
			getCurrentFilterState(),
			searchTerm,
			pageNum,
			entityType,
			weaponSeriesQuery.data
		)
		const search = params.toString()
		const url = search ? `${$page.url.pathname}?${search}` : $page.url.pathname
		// Use replaceState to update URL without adding history entry
		goto(url, { replaceState: true, noScroll: true, keepFocus: true })
	}

	// Load data
	async function loadData(pageNum: number = 1, updateUrlParam: boolean = true) {
		loading = true
		try {
			const result = await provider.loadPage(pageNum)
			data = result
			currentPage = pageNum

			// Get pagination metadata from provider
			const meta = provider.getPaginationMeta()
			if (meta) {
				total = meta.total || 0
				totalPages = meta.totalPages || 1
				// Update pageSize if provider has a different value
				if (meta.pageSize && meta.pageSize !== pageSize) {
					pageSize = meta.pageSize
				}
			}

			// Update URL to reflect current page
			if (updateUrlParam) {
				updateUrl(pageNum)
			}
		} catch (error) {
			console.error('Failed to load data:', error)
		} finally {
			loading = false
		}
	}

	// Initialize grid
	const init = (apiRef: any) => {
		api = apiRef
		// Connect provider to grid
		api.setNext(provider)

		// Intercept sort-rows to prevent client-side sorting and do server-side instead
		api.intercept('sort-rows', (ev: { key: string; add: boolean }) => {
			const { key } = ev
			const currentOrder = sortMarks[key]?.order

			// Toggle: asc -> desc -> clear
			let newSortKey: string | null = null
			let newSortOrder: 'asc' | 'desc' = 'asc'

			if (currentOrder === 'asc') {
				sortMarks = { [key]: { order: 'desc' } }
				newSortKey = key
				newSortOrder = 'desc'
			} else if (currentOrder === 'desc') {
				sortMarks = {} // Clear sort
				newSortKey = null
			} else {
				sortMarks = { [key]: { order: 'asc' } }
				newSortKey = key
				newSortOrder = 'asc'
			}

			// Update provider and reload from server
			provider.setSort(newSortKey, newSortOrder)
			loadData(1) // Reset to first page when sorting

			return false // Prevent default client-side sorting
		})

		// Add row click handler
		api.on('select-row', (ev: any) => {
			const rowId = ev.id
			if (rowId) {
				// Find the row data to get the granblueId
				const rowData = data.find((item: any) => item.id === rowId)
				if (rowData && rowData.granblueId) {
					// Store current list URL before navigating so Back button can return here
					storeListUrl($page.url.href, resource)
					goto(`/database/${resource}/${rowData.granblueId}`)
				}
			}
		})
	}

	// Handle pagination
	const handlePrevPage = () => {
		if (currentPage > 1) {
			loadData(currentPage - 1)
		}
	}

	const handleNextPage = () => {
		if (currentPage < totalPages) {
			loadData(currentPage + 1)
		}
	}

	const handlePageSizeChange = async (event: Event) => {
		const target = event.target as HTMLSelectElement
		const newPageSize = Number(target.value)
		pageSize = newPageSize // Update local state immediately
		await provider.setPageSize(newPageSize)
		loadData(1)
	}

	// Handle search with debounce
	const handleSearch = (term: string) => {
		// Clear existing timeout
		if (searchTimeout) {
			clearTimeout(searchTimeout)
		}

		const trimmed = term.trim()

		// Avoid triggering a fetch on initial mount when search is empty
		// Only clear search and reload if we previously had a non-empty query
		if (trimmed.length < 2) {
			if (lastSearchTerm !== '') {
				searchTimeout = setTimeout(() => {
					provider.clearSearch()
					lastSearchTerm = ''
					loadData(1)
				}, 300)
			}
			return
		}

		// Debounced search when user has typed enough characters
		searchTimeout = setTimeout(() => {
			lastSearchTerm = trimmed
			provider.setSearchQuery(trimmed)
			loadData(1) // Reset to first page when searching
		}, 300)
	}

	// Watch for search term changes
	$effect(() => {
		handleSearch(searchTerm)
	})

	// Computed values
	const startItem = $derived((currentPage - 1) * pageSize + 1)
	const endItem = $derived(Math.min(currentPage * pageSize, total))

	// Track if we've initialized from URL
	let urlInitialized = $state(false)

	// Initialize filters from URL (for weapons, wait for series list)
	function initializeFromUrl() {
		if (urlInitialized) return
		if (resource === 'weapons' && !weaponSeriesQuery.data) return // Wait for weapon series

		const parsed = parseFiltersFromUrl($page.url.searchParams, entityType, weaponSeriesQuery.data)

		// Set filter state
		elementFilters = parsed.element
		rarityFilters = parsed.rarity
		proficiencyFilters = parsed.proficiency
		seasonFilters = parsed.season
		seriesFilters = parsed.series
		searchTerm = parsed.searchQuery

		// Apply filters to provider
		if (
			parsed.element.length > 0 ||
			parsed.rarity.length > 0 ||
			parsed.proficiency.length > 0 ||
			parsed.season.length > 0 ||
			parsed.series.length > 0
		) {
			const seriesAsStrings =
				parsed.series.length > 0 ? parsed.series.map((s) => String(s)) : undefined

			provider.setFilters({
				element: parsed.element.length > 0 ? parsed.element : undefined,
				rarity: parsed.rarity.length > 0 ? parsed.rarity : undefined,
				series: seriesAsStrings,
				proficiency1: parsed.proficiency.length > 0 ? parsed.proficiency : undefined,
				season: parsed.season.length > 0 ? parsed.season : undefined,
				characterSeries:
					resource === 'characters' && parsed.series.length > 0
						? parsed.series.filter((s): s is number => typeof s === 'number')
						: undefined
			})
		}

		// Apply search query to provider
		if (parsed.searchQuery.length >= 2) {
			provider.setSearchQuery(parsed.searchQuery)
			lastSearchTerm = parsed.searchQuery
		}

		// Show filters panel if any filters are active from URL
		if (
			parsed.element.length > 0 ||
			parsed.rarity.length > 0 ||
			parsed.proficiency.length > 0 ||
			parsed.season.length > 0 ||
			parsed.series.length > 0
		) {
			showFilters = true
		}

		urlInitialized = true
		loadData(parsed.page, false) // Don't update URL on initial load
	}

	// Load initial data from URL params
	onMount(() => {
		// For non-weapon resources, initialize immediately
		// For weapons, wait for series query to complete
		if (resource !== 'weapons') {
			initializeFromUrl()
		}
	})

	// For weapons, initialize once series list is loaded
	$effect(() => {
		if (resource === 'weapons' && weaponSeriesQuery.data && !urlInitialized) {
			initializeFromUrl()
		}
	})

	// Clean up timeout on destroy
	onDestroy(() => {
		if (searchTimeout) {
			clearTimeout(searchTimeout)
		}
	})
</script>

<svelte:head>
	<link rel="stylesheet" href="https://cdn.svar.dev/fonts/wxi/wx-icons.css" />
</svelte:head>

<div class="grid">
	<div class="controls">
		{#if leftActions}
			{@render leftActions()}
		{/if}

		<div class="controls-right">
			{#if headerActions}
				{@render headerActions()}
			{/if}

			<Button
				variant="ghost"
				size="small"
				onclick={() => (showFilters = !showFilters)}
				class="filter-toggle {hasActiveFilters ? 'has-active' : ''}"
			>
				Filters
				{#if hasActiveFilters}
					<span class="filter-count {selectedElement ?? ''}">
						{elementFilters.length +
							rarityFilters.length +
							seriesFilters.length +
							proficiencyFilters.length +
							seasonFilters.length}
					</span>
				{/if}
			</Button>

			<input type="text" placeholder="Search..." bind:value={searchTerm} />
		</div>
	</div>

	{#if showFilters}
		<div class="filters-row">
			<CollectionFilters
				entityType={resource === 'characters'
					? 'character'
					: resource === 'summons'
						? 'summon'
						: 'weapon'}
				bind:elementFilters
				bind:rarityFilters
				bind:seriesFilters
				bind:proficiencyFilters
				bind:seasonFilters
				onFiltersChange={handleFiltersChange}
				showSort={false}
				contained={false}
			/>
		</div>
	{/if}

	<div class="grid-wrapper" class:loading>
		{#if loading}
			<div class="loading-overlay">
				<div class="loading-spinner">Loading...</div>
			</div>
		{/if}

		<Grid
			{data}
			{columns}
			{init}
			{sortMarks}
			sizes={{ rowHeight: 80 }}
			class="database-grid-theme"
		/>
	</div>

	<div class="grid-footer">
		<div class="pagination-info">
			{#if total > 0}
				Showing {startItem} to {endItem} of {total} entries
			{:else}
				No entries found
			{/if}
		</div>

		<div class="pagination-controls">
			<button class="pagination-button" onclick={handlePrevPage} disabled={currentPage <= 1}>
				Previous
			</button>

			<span class="page-display">
				Page {currentPage} of {totalPages}
			</span>

			<button
				class="pagination-button"
				onclick={handleNextPage}
				disabled={currentPage >= totalPages}
			>
				Next
			</button>
		</div>
	</div>
</div>

<style lang="scss">
	@use '$src/themes/effects' as effects;
	@use '$src/themes/layout' as layout;
	@use '$src/themes/spacing' as spacing;
	@use '$src/themes/typography' as typography;

	.grid {
		width: 100%;
		background: var(--card-bg);
		border: 0.5px solid rgba(0, 0, 0, 0.18);
		border-radius: layout.$page-corner;
		box-shadow: effects.$page-elevation;
		overflow: hidden;

		.controls {
			display: flex;
			flex-wrap: wrap;
			align-items: center;
			justify-content: space-between;
			padding: spacing.$unit;
			gap: spacing.$unit;

			.controls-right {
				display: flex;
				align-items: center;
				gap: spacing.$unit;
				margin-left: auto;

				:global(.filter-toggle) {
					gap: spacing.$unit-half;

					:global(svg) {
						transition: transform 0.15s ease;
					}

					&:global(.has-active) {
						color: var(--accent-color);
					}
				}

				.filter-count {
					display: inline-flex;
					align-items: center;
					justify-content: center;
					min-width: 18px;
					height: 18px;
					margin-left: spacing.$unit;
					padding: 0 spacing.$unit-half;
					background: var(--accent-color);
					color: white;
					font-size: 11px;
					font-weight: typography.$medium;
					border-radius: 9px;

					// Element-colored badges
					&:global(.wind) {
						background: var(--wind-button-bg);
					}
					&:global(.fire) {
						background: var(--fire-button-bg);
					}
					&:global(.water) {
						background: var(--water-button-bg);
					}
					&:global(.earth) {
						background: var(--earth-button-bg);
					}
					&:global(.dark) {
						background: var(--dark-button-bg);
					}
					&:global(.light) {
						background: var(--light-button-bg);
					}
				}

				input {
					padding: spacing.$unit spacing.$unit-2x;
					background: var(--input-bound-bg);
					border: none;
					border-radius: layout.$item-corner;
					font-family: 'AGrot', system-ui, sans-serif;
					font-size: typography.$font-small;
					width: 200px;

					&:hover {
						background: var(--input-bound-bg-hover);
					}

					&:focus {
						outline: none;
						border-color: #007bff;
					}
				}
			}
		}

		.filters-row {
			display: flex;
			align-items: center;
			padding: 0 spacing.$unit spacing.$unit spacing.$unit;
			border-bottom: 1px solid #e5e5e5;
			background: white;

			:global(.filters-container) {
				flex: 1;
				min-width: 0;

				// Override filter trigger padding
				:global([data-select-trigger]) {
					padding-top: 7px;
					padding-bottom: 7px;
				}
			}
		}

		.grid-wrapper {
			position: relative;
			overflow-x: auto;
			min-height: 400px;

			&.loading {
				opacity: 0.6;
			}

			.loading-overlay {
				position: absolute;
				top: 0;
				left: 0;
				right: 0;
				bottom: 0;
				background: rgba(255, 255, 255, 0.9);
				display: flex;
				align-items: center;
				justify-content: center;
				z-index: 10;

				.loading-spinner {
					font-size: typography.$font-medium;
					color: #666;
				}
			}
		}

		.grid-footer {
			display: flex;
			justify-content: space-between;
			align-items: center;
			padding: spacing.$unit;
			border-top: 1px solid #e5e5e5;
			background: #f8f9fa;

			.pagination-info {
				font-size: typography.$font-small;
				color: #6c757d;
			}

			.pagination-controls {
				display: flex;
				align-items: center;
				gap: spacing.$unit;

				.pagination-button {
					padding: spacing.$unit * 0.5 spacing.$unit;
					background: white;
					border: 1px solid #ddd;
					border-radius: 4px;
					font-size: typography.$font-small;
					cursor: pointer;
					transition: all 0.2s;

					&:hover:not(:disabled) {
						background: #e9ecef;
						border-color: #adb5bd;
					}

					&:disabled {
						opacity: 0.5;
						cursor: not-allowed;
					}
				}

				.page-display {
					font-size: typography.$font-small;
					color: #495057;
					min-width: 100px;
					text-align: center;
				}
			}
		}
	}

	// Global styles for SVAR Grid elements
	:global(.database-grid-theme) {
		font-size: typography.$font-small;
		width: 100%;
	}

	:global(.database-grid .wx-table-box) {
		width: 100%;
		max-width: 100%;
	}

	:global(.wx-grid .wx-header) {
		background: #f8f9fa;
	}

	:global(.wx-grid .wx-h-row) {
		background: #f8f9fa;
		border-bottom: 1px solid #e5e5e5;
	}

	:global(.wx-grid .wx-header-cell) {
		background: #f8f9fa;
		font-weight: typography.$bold;
		color: #495057;
		border-bottom: 2px solid #dee2e6;
		border-radius: layout.$item-corner;
		transition: background-color 0.15s ease;
		cursor: pointer;

		&:hover {
			background: #e9ecef;
		}
	}

	:global(.wx-grid .wx-cell) {
		padding: spacing.$unit * 0.5;
		vertical-align: middle;
		display: flex;
		align-items: center;
		border: none;
		--wx-table-cell-border: none;
	}

	:global(.wx-grid .wx-cell:first-child) {
		padding-left: spacing.$unit-2x;
	}

	:global(.wx-grid .wx-cell:not(:last-child)) {
		border-right: none;
	}

	:global(.wx-grid .wx-row:hover) {
		background: #f8f9fa;
		cursor: pointer;
	}

	// Element color classes
	:global(.element-fire) {
		color: #ff6b6b;
	}
	:global(.element-water) {
		color: #4dabf7;
	}
	:global(.element-earth) {
		color: #51cf66;
	}
	:global(.element-wind) {
		color: #69db7c;
	}
	:global(.element-light) {
		color: #ffd43b;
	}
	:global(.element-dark) {
		color: #845ef7;
	}

	// Database image styling - removed to allow cells to control sizing
</style>
