<script lang="ts">
	import type { CharacterWithRelations } from '$lib/server/daily-character';
	import { onMount } from 'svelte';
	import { language, t } from '$lib/i18n';

	let {
		characters,
		selectedCharacters,
		onSelect
	}: {
		characters: CharacterWithRelations[];
		selectedCharacters: CharacterWithRelations[];
		onSelect: (character: CharacterWithRelations) => void;
	} = $props();

	const state = $state({
		searchInput: '',
		highlightedIndex: 0,
		dropdownContainer: null as HTMLDivElement | null,
		searchContainer: null as HTMLDivElement | null
	});

	const isFrench = $derived($language === 'fr');

	function parseEpithets(value: unknown): string[] {
		if (Array.isArray(value)) {
			return value.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
		}

		if (typeof value === 'string') {
			try {
				const parsed = JSON.parse(value);
				if (Array.isArray(parsed)) {
					return parsed.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
				}
			} catch {
				if (value.length > 0) {
					return [value];
				}
			}
		}

		return [];
	}

	function getDisplayName(character: CharacterWithRelations): string {
		if (isFrench && typeof character.frName === 'string' && character.frName.length > 0) {
			return character.frName;
		}

		return character.name;
	}

	function getDisplayEpithets(character: CharacterWithRelations): string[] {
		const frenchEpithets = parseEpithets(character.frEpithets);
		if (isFrench && frenchEpithets.length > 0) {
			return frenchEpithets;
		}

		return parseEpithets(character.epithets);
	}

	function normalizeSearchText(value: string): string {
		return value
			.normalize('NFD')
			.replace(/[\u0300-\u036f]/g, '')
			.toLowerCase();
	}
	
	onMount(() => {
		// Add click outside listener
		document.addEventListener('click', handleClickOutside);
		
		return () => {
			document.removeEventListener('click', handleClickOutside);
		};
	});
	
	const filteredCharacters = $derived.by(() => {
		const searchTerm = normalizeSearchText(state.searchInput);

		return characters.filter((char) => {
			const displayName = getDisplayName(char);
			const displayEpithets = getDisplayEpithets(char);
			const nameMatches = normalizeSearchText(displayName).includes(searchTerm);
			const epithetsMatches = displayEpithets.some((epithet) =>
				normalizeSearchText(epithet).includes(searchTerm)
			);

			return (nameMatches || epithetsMatches) &&
				!selectedCharacters.some((selected) => selected.id === char.id);
		});
	});

	// Reset highlighted index when filtered list changes.
	$effect(() => {
		const nextFilteredCharacters = filteredCharacters;
		if (!nextFilteredCharacters) {
			return;
		}
		state.highlightedIndex = 0;
	});

	// Scroll highlighted item into view.
	$effect(() => {
		const nextFilteredCharacters = filteredCharacters;

		if (!state.dropdownContainer || state.highlightedIndex < 0) {
			return;
		}

		if (state.highlightedIndex >= nextFilteredCharacters.length) {
			return;
		}

		const highlightedButton = state.dropdownContainer.querySelector(
			`button:nth-child(${state.highlightedIndex + 1})`
		) as HTMLElement | null;

		highlightedButton?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
	});

	function selectCharacter(character: CharacterWithRelations) {
		onSelect(character);
		state.searchInput = '';
		state.highlightedIndex = 0;
	}

	function handleKeydown(event: KeyboardEvent) {
		if (filteredCharacters.length === 0) return;

		switch (event.key) {
			case 'ArrowDown':
				event.preventDefault();
				state.highlightedIndex = Math.min(
					state.highlightedIndex + 1,
					filteredCharacters.length - 1
				);
				break;
			case 'ArrowUp':
				event.preventDefault();
				state.highlightedIndex = Math.max(state.highlightedIndex - 1, 0);
				break;
			case 'Enter':
				event.preventDefault();
				if (filteredCharacters[state.highlightedIndex]) {
					selectCharacter(filteredCharacters[state.highlightedIndex]);
				}
				break;
		}
	}

	function submitGuess() {
		if (filteredCharacters.length === 0) return;
		const characterToSelect = filteredCharacters[state.highlightedIndex] ?? filteredCharacters[0];
		if (characterToSelect) {
			selectCharacter(characterToSelect);
		}
	}
	
	function handleClickOutside(event: MouseEvent) {
		if (state.searchContainer && !state.searchContainer.contains(event.target as Node)) {
			state.searchInput = '';
		}
	}
</script>

<div class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur z-10">
	<h2 class="text-sm font-semibold uppercase tracking-[0.2em] text-amber-100">{$t.game.components.searchInput.title}</h2>
	<div class="mt-4 flex flex-col gap-3 sm:flex-row">
		<div bind:this={state.searchContainer} class="relative w-full">
			<input
				bind:value={state.searchInput}
				class="w-full rounded-full border border-amber-200/30 bg-slate-900/60 px-5 py-3 text-sm text-slate-100 placeholder:text-slate-400 focus:border-amber-200/70 focus:outline-none"
				placeholder={$t.game.components.searchInput.placeholder}
				type="text"
				onkeydown={handleKeydown}
			/>
			{#if state.searchInput.length > 0 && filteredCharacters.length > 0}
				<div bind:this={state.dropdownContainer} class="absolute top-full left-0 right-0 mt-2 max-h-96 overflow-y-auto rounded-2xl border border-amber-200/30 bg-slate-900/90 backdrop-blur z-10">
					{#each filteredCharacters as character, index (character.id)}
						<button
							class="w-full px-5 py-4 text-left text-base text-slate-100 transition border-b border-slate-800/50 last:border-b-0 flex items-center gap-4 {index === state.highlightedIndex ? 'bg-slate-700' : 'hover:bg-slate-800/70'}"
							type="button"
							onmouseenter={() => (state.highlightedIndex = index)}
							onclick={() => selectCharacter(character)}
						>
							{#if character.pictureUrl}
								<img
									src={character.pictureUrl}
									alt={getDisplayName(character)}
									loading="lazy"
									class="w-12 h-12 rounded-full object-cover border border-amber-200/30"
								/>
							{:else}
								<div class="w-12 h-12 rounded-full bg-slate-800 border border-amber-200/30 flex items-center justify-center">
									<span class="text-xs text-slate-400">?</span>
								</div>
							{/if}
							<div class="flex-1">
								<span class="font-semibold text-amber-100">{getDisplayName(character)}</span>
								{#if getDisplayEpithets(character).length > 0}
										<span class="ml-2 text-xs text-slate-400">
											• {getDisplayEpithets(character).join(', ')}
										</span>
								{/if}
							</div>
						</button>
					{/each}
				</div>
			{/if}
		</div>
		<button
			type="button"
			onclick={submitGuess}
			disabled={state.searchInput.length === 0 || filteredCharacters.length === 0}
			class="rounded-full bg-amber-300 px-6 py-3 text-sm font-semibold text-slate-900 transition hover:bg-amber-200 disabled:cursor-not-allowed disabled:opacity-50"
		>
			{$t.game.components.searchInput.submit}
		</button>
	</div>
</div>
