<script lang="ts">
	import Textbox from '$lib/components/ui/form-elements/Textbox.svelte';
	import { onMount } from 'svelte';
	import { fade } from 'svelte/transition';
	import { goto } from '$app/navigation';

	import { LOCATIONS } from '$lib/locations-object.svelte';
	import type { ApiLocationFilter } from '@owlboard/api-schema-types';

	let { value = $bindable() } = $props();

	let showResults = $state(false);
	let selectedIndex = $state(-1);

	const MAX_RESULTS = 5;

	function tokenize(query: string) {
		return query.toLowerCase().trim().split(/\s+/).filter(Boolean);
	}

	let results = $derived.by(() => {
		if (value.length < 3) return [];

		const tokens = tokenize(value);
		const lowerQuery = value.toLowerCase().trim();

		return LOCATIONS.data
			.filter((r) => tokens.every((t) => r.s.includes(t)))
			.sort((a, b) => {
				// Priority One - Exact CRS Match
				const aExactCrs = a.c?.toLowerCase() === lowerQuery;
				const bExactCrs = b.c?.toLowerCase() === lowerQuery;
				if (aExactCrs && !bExactCrs) return -1;
				if (!aExactCrs && bExactCrs) return 1;

				// Priority Two - Exact Name Match
				const aNameLow = a.n.toLowerCase();
				const bNameLow = b.n.toLowerCase();
				const aExactName = aNameLow === lowerQuery;
				const bExactName = bNameLow === lowerQuery;
				if (aExactName !== bExactName) return aExactName ? -1 : 1;

				// Priority Three - Name starts with Query
				const aStarts = aNameLow.startsWith(lowerQuery);
				const bStarts = bNameLow.startsWith(lowerQuery);
				if (aStarts !== bStarts) return aStarts ? -1 : 1;

				// Priority Four - 'Stations' with CRS
				if (!!a.c && !b.c) return -1;
				if (!a.c && !!b.c) return 1;

				// Alphabetical Sort
				return a.n.localeCompare(b.n);
			})
			.slice(0, MAX_RESULTS);
	});

	$effect(() => {
		if (results) selectedIndex = -1;
	});

	// Hide results when click outside of container
	$effect(() => {
		if (showResults) {
			const onClick = (e: MouseEvent) => {
				const target = e.target as HTMLElement;
				if (!target.closest('.location-search')) {
					showResults = false;
				}
			};

			document.addEventListener('click', onClick);
			return () => document.removeEventListener('click', onClick);
		}
	});

	function choose(loc: ApiLocationFilter.LocationFilterObject) {
		showResults = false;
		selectedIndex = -1;
		value = '';
		console.log('Selected Location: ', JSON.stringify(loc));
		const queryString = loc.c || loc.t;
		goto(`/board?loc=${queryString.toLowerCase()}`);
	}

	function handleKey(e: KeyboardEvent) {
		if (!results.length) return;

		if (e.key === 'ArrowDown') {
			e.preventDefault();
			selectedIndex = Math.min(selectedIndex + 1, results.length - 1);
		}

		if (e.key === 'ArrowUp') {
			e.preventDefault();
			selectedIndex = Math.max(selectedIndex - 1, 0);
		}

		if (e.key === 'Enter' && selectedIndex >= 0) {
			choose(results[selectedIndex]);
		}
	}
</script>

<div class="location-search">
	<Textbox
		bind:value
		placeholder="Enter Location"
		oninput={() => (showResults = true)}
		onkeydown={handleKey}
		capital
	/>

	{#if showResults && results.length}
		<ul
			id="location-results"
			popover={showResults && results.length ? 'manual' : null}
			role="listbox"
			class="suggestions"
			transition:fade={{ duration: 200 }}
		>
			{#each results as loc, i}
				<li class="result-item" class:selected={i === selectedIndex} onclick={() => choose(loc)}>
					<div class="crs-badge-container">
						{#if loc.c}
							<span class="crs-badge">{loc.c}</span>
						{/if}
					</div>
					<div class="details">
						<span class="name">{loc.n || loc.t}</span>
						<span class="tiploc">{loc.t}</span>
					</div>
				</li>
			{/each}
		</ul>
	{/if}
</div>

<style>
	.location-search {
		position: relative;
		width: 100%;
	}

	.suggestions[popover] {
		position: absolute;
		inset: unset;
		margin: 0;
		margin-top: 3px;
		border: none;
		border-radius: 5px;
		padding: 0;
		width: 100%;
		max-height: 350px;
		top: 100%;
		background-color: var(--color-title);
		color: var(--color-bg-dark);
		box-shadow: var(--shadow-std);
		display: block;
		z-index: 9999;
	}

	.suggestions:not([popover]) {
		display: none;
	}

	.result-item {
		font-family: 'URW Gothic', sans-serif;
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0.5rem 1rem;
		cursor: pointer;
		min-height: 48px;
		transition: all 0.15s;
		border-bottom: 1px solid rgba(0, 0, 0, 0.4);
	}

	.result-item.selected,
	.result-item:hover {
		background-color: var(--color-accent);
		color: var(--color-title);
	}

	.crs-badge {
		font-family: ui-monospace, monospace;
		font-size: 1.1rem;
		font-weight: 700;
		background: var(--color-accent);
		color: var(--color-title);
		padding: 3px 6px;
		border-radius: 10px;
		transition: all 0.1s;
	}

	.crs-badge.empty {
		filter: opacity(0);
	}

	.result-item:hover .crs-badge {
		filter: brightness(1.3);
	}

	.details {
		display: flex;
		flex-direction: column;
	}

	.name {
		font-size: 1.1rem;
		font-weight: 700;
		text-align: right;
		text-transform: capitalize;
	}

	.tiploc {
		text-align: right;
		font-size: 0.8rem;
	}
</style>
