<script lang="ts">
	import { createEventDispatcher, onDestroy, tick } from 'svelte';

	import { type GeoLocation } from '$lib/stores/settings';

	import * as Alert from '$lib/components/ui/alert';
	import { Button } from '$lib/components/ui/button';
	import { Input } from '$lib/components/ui/input';
	import * as Popover from '$lib/components/ui/popover';

	export let label: string = 'Search location...';
	export let placeholder: string = 'Enter city name...';

	interface ResultSet {
		results: GeoLocation[] | undefined;
	}

	const dispatch = createEventDispatcher();
	let debounceTimeout: ReturnType<typeof setTimeout> | undefined;
	let searchQuery = '';
	let popoverOpen = false;
	let searchInputEl: HTMLInputElement | null = null;

	onDestroy(() => {
		clearTimeout(debounceTimeout);
	});

	const closePopover = () => {
		popoverOpen = false;
	};

	const selectLocation = (location: GeoLocation) => {
		searchQuery = '';
		closePopover();
		dispatch('location', location);
	};

	async function focusInput() {
		await tick();
		searchInputEl?.focus();
	}

	$: if (popoverOpen) {
		focusInput();
	}

	$: results = (async () => {
		if (debounceTimeout) {
			clearTimeout(debounceTimeout);
		}
		if (searchQuery.length < 2) {
			return { results: [] };
		}
		await new Promise((resolve) => {
			debounceTimeout = setTimeout(resolve, 300);
		});

		if (searchQuery.toLowerCase() == 'gps') {
			let position: GeolocationPosition = await new Promise((resolve, reject) =>
				navigator.geolocation.getCurrentPosition(resolve, reject, {})
			);
			const latitude = position.coords.latitude;
			const longitude = position.coords.longitude;
			return {
				results: [
					{
						id: 100000000 + Math.floor(latitude * 100 + longitude + 1000),
						name: `GPS ${latitude.toFixed(2)}°N ${longitude.toFixed(2)}°E`,
						latitude: latitude,
						longitude: longitude,
						elevation: position.coords.altitude ?? NaN,
						feature_code: '',
						country_code: undefined,
						admin1_id: undefined,
						admin3_id: undefined,
						admin4_id: undefined,
						timezone: '',
						population: undefined,
						postcodes: undefined,
						country_id: undefined,
						country: undefined,
						admin1: undefined,
						admin3: undefined,
						admin4: undefined
					}
				]
			};
		}

		const url = 'https://geocoding-api.open-meteo.com/v1/search';
		const fetchUrl = `${url}?${new URLSearchParams({ name: searchQuery })}`;
		const result = await fetch(fetchUrl);

		if (!result.ok) {
			throw new Error(await result.text());
		}

		return (await result.json()) as ResultSet;
	})();
</script>

<Popover.Root bind:open={popoverOpen}>
	<Popover.Trigger
		class="flex h-9 w-full cursor-pointer items-center gap-2 rounded-md border border-border bg-background px-3 text-[0.8125rem] text-muted-foreground transition-[border-color,box-shadow] duration-150 hover:border-primary"
	>
		<svg
			class="h-3.5 w-3.5 shrink-0 opacity-50"
			xmlns="http://www.w3.org/2000/svg"
			fill="none"
			viewBox="0 0 24 24"
			stroke="currentColor"
			stroke-width="2"
		>
			<circle cx="11" cy="11" r="8" />
			<path d="m21 21-4.3-4.3" />
		</svg>
		<span class="overflow-hidden text-ellipsis whitespace-nowrap">{label}</span>
	</Popover.Trigger>

	<Popover.Content
		class="popover-dropdown w-(--bits-popover-anchor-width) min-w-[320px] p-0"
		side="bottom"
		align="start"
		sideOffset={4}
		onOpenAutoFocus={(e) => {
			e.preventDefault();
			focusInput();
		}}
	>
		<div class="flex flex-col">
			<div class="p-3">
				<div class="flex gap-2">
					<div class="flex-1">
						<Input
							type="search"
							{placeholder}
							class="h-9"
							autocomplete="off"
							spellcheck="false"
							aria-label="Search Location"
							bind:value={searchQuery}
							bind:ref={searchInputEl}
						/>
					</div>
					<Button
						variant="outline"
						size="default"
						class="h-9 px-2.5"
						title="Use GPS Location"
						onclick={() => (searchQuery = 'GPS')}
					>
						<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
							<path
								stroke-linecap="round"
								stroke-linejoin="round"
								stroke-width="2"
								d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
							/>
							<path
								stroke-linecap="round"
								stroke-linejoin="round"
								stroke-width="2"
								d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
							/>
						</svg>
					</Button>
				</div>
			</div>

			<div class="max-h-[min(400px,50vh)] overflow-y-auto px-3 pb-3">
				{#await results}
					<div class="flex h-20 items-center justify-center">
						<div class="flex items-center space-x-2">
							<div class="h-4 w-4 animate-spin rounded-full border-b-2 border-primary"></div>
							<span class="text-sm text-muted-foreground">Searching...</span>
						</div>
					</div>
				{:then results}
					{#if results.results && results.results.length === 0}
						{#if searchQuery.length < 2}
							<div
								class="flex items-start gap-2 rounded-md bg-primary/8 p-2.5 text-muted-foreground"
							>
								<svg
									class="mt-0.5 h-3.5 w-3.5 shrink-0"
									fill="none"
									stroke="currentColor"
									viewBox="0 0 24 24"
									stroke-width="2"
								>
									<path
										stroke-linecap="round"
										stroke-linejoin="round"
										d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
									/>
								</svg>
								<span class="text-xs">
									Start typing to search or use GPS to detect your position
								</span>
							</div>
						{:else}
							<Alert.Root
								class="border-orange-200 bg-orange-50 dark:border-orange-800 dark:bg-orange-900/20"
							>
								<Alert.Description class="text-orange-700 dark:text-orange-300">
									No locations found for "{searchQuery}". Try a different term.
								</Alert.Description>
							</Alert.Root>
						{/if}
					{:else if !results.results}
						<Alert.Root variant="destructive">
							<Alert.Description>No locations found</Alert.Description>
						</Alert.Root>
					{:else}
						<div class="space-y-0.5">
							{#each results.results || [] as location, i (i)}
								<button
									class="group block w-full cursor-pointer rounded-md border border-transparent bg-transparent px-2.5 py-2 transition-[background,border-color] duration-150 hover:border-border hover:bg-accent"
									onclick={() => selectLocation(location)}
								>
									<div class="flex items-center gap-2.5">
										<img
											class="h-7 w-7 rounded-full"
											src="/images/country-flags/{(
												location.country_code || 'united_nations'
											).toLowerCase()}.svg"
											alt={location.country}
										/>
										<div class="flex-1 text-left">
											<div class="text-sm font-medium text-foreground">
												{location.name}
											</div>
											<div class="text-xs text-muted-foreground">
												{location.admin1 || ''}
												{location.country || ''}
												· {location.latitude.toFixed(2)}°N {location.longitude.toFixed(2)}°E
												{#if location.elevation}· {location.elevation.toFixed(0)}m{/if}
											</div>
										</div>
										<svg
											class="h-3.5 w-3.5 text-muted-foreground opacity-0 transition-opacity duration-150 group-hover:opacity-100"
											fill="none"
											stroke="currentColor"
											viewBox="0 0 24 24"
										>
											<path
												stroke-linecap="round"
												stroke-linejoin="round"
												stroke-width="2"
												d="M9 5l7 7-7 7"
											/>
										</svg>
									</div>
								</button>
							{/each}
						</div>
					{/if}
				{:catch error}
					<Alert.Root variant="destructive">
						<Alert.Description>Error: {error.message}</Alert.Description>
					</Alert.Root>
				{/await}
			</div>
		</div>
	</Popover.Content>
</Popover.Root>
