<script lang="ts">
	import { type Component } from "svelte";
	import { searchbar, view } from "./view.svelte";
	import { map } from "./map.svelte";
	import Input from "../ui/input/input.svelte";
	import {
		EllipsisIcon,
		HomeIcon,
		SettingsIcon,
		UserIcon,
	} from "@lucide/svelte";
	import Button from "../ui/button/button.svelte";
	import { search, type Feature } from "$lib/services/Search";
	import RequiresCapability from "./RequiresCapability.svelte";
	import {
		advertiseRemoteLocation,
		location,
		remoteLocation,
	} from "./location.svelte";
	import * as Popover from "../ui/popover";
	import { routing } from "$lib/services/navigation/routing.svelte";
	import InRouteSidebar from "./sidebar/InRouteSidebar.svelte";
	import { m } from "$lang/messages";
	import LoadingSidebar from "./sidebar/LoadingSidebar.svelte";
	import { Tween } from "svelte/motion";
	import { quintOut } from "svelte/easing";
	import Progressbar from "../Progressbar.svelte";
	import { postHazard } from "$lib/services/lnv";
	import { fetchHazards } from "./hazards.svelte";

	const views: Record<string, string> = {
		main: "MainSidebar",
		info: "InfoSidebar",
		route: "RouteSidebar",
		trip: "TripSidebar",
		search: "SearchSidebar",
		user: "UserSidebar",
		settings: "settings/SettingsSidebar",
		about: "settings/AboutSidebar",
		"offline-maps": "settings/OfflineMapsSidebar",
		"dev-options": "settings/DeveloperSidebar",
		language: "settings/LanguageSidebar",
		onboarding: "onboarding/OnboardingSidebar",
		"onboarding-vehicles": "onboarding/OnboardingVehiclesSidebar",
		"nearby-poi": "NearbyPOISidebar",
		licenses: "settings/LicensesSidebar",
	};

	const fullscreen: Record<string, boolean> = {
		main: false,
		info: false,
		route: true,
		trip: true,
		search: false,
		user: false,
		settings: true,
		about: true,
		"offline-maps": true,
		"dev-options": true,
		language: true,
		onboarding: true,
		"onboarding-vehicles": true,
		"nearby-poi": false,
		licenses: true,
	};

	let isDragging = false;
	let startY = 0;
	let startHeight = 0;
	let sidebarHeight = new Tween(200, {
		duration: 500,
		easing: quintOut,
	});
	let lastSidebarHeight = 0;
	$effect(() => {
		if (lastSidebarHeight != sidebarHeight.current) {
			lastSidebarHeight = sidebarHeight.current;
			map.updateMapPadding();
		}
	});
	let previousHeight = 200;
	let hideSearch = $state(false);

	let CurrentView: Component = $state(LoadingSidebar);
	let currentProps = $state({});
	const modules = import.meta.glob("./sidebar/**/*.svelte");
	$effect(() => {
		const path =
			modules[
				`./sidebar/${views[view.current.type] || "InvalidSidebar"}.svelte`
			];
		if (!path) {
			// Invalid view
			import("./sidebar/InvalidSidebar.svelte").then((v) => {
				CurrentView = v.default;
			});
			return;
		}
		path().then((m) => {
			CurrentView = (m as { default: Component }).default;
			currentProps = view.current.props || {};
		});
	});
	$effect(() => {
		const fullHeight = window.innerHeight - 20 - 40 - 10;
		if (fullscreen[view.current.type] && !routing.currentTrip) {
			if (sidebarHeight.target != fullHeight) {
				if (window.innerWidth < 768) {
					hideSearch = true;
					map.ignorePadding = true;
				}
				previousHeight = sidebarHeight.target;
				sidebarHeight.target = fullHeight;
				requestAnimationFrame(() => {
					map.updateMapPadding();
				});
			}
		} else {
			hideSearch = false;
			if (sidebarHeight.target == fullHeight) {
				map.ignorePadding = false;
				sidebarHeight.target = previousHeight;
				requestAnimationFrame(() => {
					map.updateMapPadding();
				});
			}
		}
	});

	function debounce<T>(getter: () => T, delay: number): () => T | undefined {
		let value = $state<T>();
		let timer: NodeJS.Timeout;
		$effect(() => {
			const newValue = getter(); // read here to subscribe to it
			clearTimeout(timer);
			timer = setTimeout(() => (value = newValue), delay);
			return () => clearTimeout(timer);
		});
		return () => value;
	}

	let searchText = $derived.by(debounce(() => searchbar.text, 300));
	let searchResults: Feature[] = $state([]);
	let mobileView = $derived(window.innerWidth < 768 || routing.currentTrip);

	$effect(() => {
		if (!searchText) {
			searchResults = [];
			if (view.current.type == "search") view.switch("main");
			return;
		}
		if (searchText.length > 0) {
			view.loading = true;
			search(searchText, 0, 0).then((results) => {
				searchResults = results;
				view.loading = false;
				view.switch("search", {
					results: searchResults,
					query: searchText,
				});
			});
		} else {
			searchResults = [];
		}
	});
</script>

{#if !routing.currentTrip && !hideSearch}
	<div id="floating-search" class={mobileView ? "mobileView" : ""}>
		<Input class="h-10" placeholder="Search..." bind:value={searchbar.text} />
	</div>
{/if}
<div
	id="sidebar"
	class={mobileView ? "mobileView" : ""}
	style={(mobileView ? `height: ${sidebarHeight.current}px;` : "") +
		(routing.currentTrip ? "bottom: 0 !important;" : "")}
>
	{#if mobileView}
		<div
			style="position: absolute; top: 0; left: 0; right: 0; height: 20px; touch-action: none; cursor: grab;"
			role="button"
			id="grabber"
			ontouchstart={(e) => {
				isDragging = true;
				startY = e.touches[0].clientY;
				startHeight = sidebarHeight.target;
			}}
			ontouchmove={(e) => {
				if (!isDragging) return;
				e.preventDefault();
				const deltaY = e.touches[0].clientY - startY;
				let newHeight = Math.max(100, startHeight - deltaY);

				const snapPoint = 200;
				const snapThreshold = 20;
				if (Math.abs(newHeight - snapPoint) < snapThreshold) {
					newHeight = snapPoint;
				}
				sidebarHeight.target = newHeight;

				map.updateMapPadding();
			}}
			ontouchend={() => {
				if (!isDragging) return;
				isDragging = false;
			}}
		></div>
		<div
			style="height: 10px; display: flex; justify-content: center; align-items: center;"
		>
			<div
				style="height: 8px; background-color: #acacac; width: 40%; border-radius: 15px;"
			></div>
		</div>
	{/if}

	{#if view.loading}
		<Progressbar />
	{/if}

	{#if routing.currentTrip}
		<InRouteSidebar />
	{:else}
		<CurrentView {...currentProps}></CurrentView>
	{/if}
</div>
{#if !routing.currentTrip}
	<div id="navigation" class={mobileView ? "mobileView" : ""}>
		<button onclick={() => view.switch("main")}>
			<HomeIcon />
		</button>
		<RequiresCapability capability="auth">
			<button
				onclick={async () => {
					view.switch("user");
				}}
			>
				<UserIcon />
			</button>
		</RequiresCapability>
		<button
			onclick={() => {
				view.switch("settings");
			}}
		>
			<SettingsIcon />
		</button>
		<!-- <button onclick={() => {
			location.toggleLock();
		}}>
			L
		</button> -->
		<Popover.Root>
			<Popover.Trigger>
				<button>
					<EllipsisIcon />
				</button>
			</Popover.Trigger>
			<Popover.Content>
				<div class="flex flex-col gap-2">
					<Button
						variant="outline"
						onclick={() => {
							location.toggleLock();
						}}
					>
						{location.locked ? m["location.unlock"]() : m["location.unlock"]()}
					</Button>
					{#if location.code}
						<span>{m["location.code"]()}: {location.code}</span>
					{/if}
					<Button
						variant="outline"
						onclick={() => {
							advertiseRemoteLocation();
						}}
					>
						{m["location.start"]()}
					</Button>
					<Button
						variant="outline"
						onclick={() => {
							remoteLocation(prompt("Code?") || "");
						}}
					>
						{m["location.join"]()}
					</Button>
					<Button
						variant="outline"
						onclick={async () => {
							await postHazard(
								{ lat: location.lat, lon: location.lng },
								"bumpy-road",
							);
							alert("Thanks for your report!");
							await fetchHazards();
						}}
					>
						Report Bumpy Road
					</Button>
				</div>
			</Popover.Content>
		</Popover.Root>
	</div>
{/if}

<style>
	#sidebar {
		background-color: hsla(0, 0%, 5%, 0.6);
		backdrop-filter: blur(5px);
		color: white;
		padding: 15px;
		width: calc(25% - 20px);
		max-width: calc(25% - 20px);
		overflow-y: scroll;
		height: calc(100vh - 20px - 40px - 10px - 50px);
		max-height: calc(100vh - 20px);

		position: fixed;
		top: calc(40px + 10px);
		left: 0;
		z-index: 10;
		margin: 10px;
		border-radius: 15px;
		border-bottom-left-radius: 0;
		border-bottom-right-radius: 0;
	}

	#floating-search {
		position: fixed;
		margin: 10px;
		margin-top: calc(10px + env(safe-area-inset-top));
		top: 0;
		left: 0;
		z-index: 20;
		width: calc(25% - 20px);
		background-color: hsla(0, 0%, 5%, 0.6);
		backdrop-filter: blur(5px);
		border-radius: 10px;
	}

	#navigation {
		position: fixed;
		bottom: 0;
		left: 0;
		z-index: 50;
		background-color: hsla(0, 0%, 5%, 0.9);
		backdrop-filter: blur(5px);
		margin-bottom: 0;
		padding: 10px;
		padding-bottom: calc(10px + env(safe-area-inset-bottom));
		margin: 10px;
		width: calc(25% - 20px);
		/* border-top-left-radius: 15px;
		border-top-right-radius: 15px; */
		height: 50px;
		border-bottom-left-radius: 15px;
		border-bottom-right-radius: 15px;

		display: flex;
		justify-content: space-around;
	}

	/* mobile view */
	#sidebar.mobileView {
		position: fixed;
		top: unset;
		bottom: calc(50px + env(safe-area-inset-bottom));
		left: 0;
		/* min-width: calc(100% - 20px);
		max-width: calc(100% - 20px); */
		min-width: calc(100%);
		max-width: calc(100%);
		width: calc(100% - 20px);
		height: 200px;
		margin: unset;
		/* margin-left: 10px;
		margin-right: 10px; */
		border-radius: 0;
		border-top-left-radius: 15px;
		border-top-right-radius: 15px;
		padding-top: 5px; /* for the grabber */
	}

	#floating-search.mobileView {
		width: calc(100% - 20px);
	}

	#navigation.mobileView {
		margin: 0;
		width: calc(100%);
		border-bottom-left-radius: 0;
		border-bottom-right-radius: 0;
		padding-bottom: calc(40px + env(safe-area-inset-bottom));
	}
</style>
