<script lang="ts">
	import type { CheckData, CheckResponse, ClientGameData, MapData } from "$lib/types";
	import { page } from "$app/stores";
	import { onMount } from "svelte";
	import L from "leaflet";

	import "leaflet/dist/leaflet.css";
	import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
	import "leaflet-defaulticon-compatibility";

	const zoom = 13;
	const center: [number, number] = [45.742858495, 4.86163814];

	const metroIcon = new L.Icon({ iconUrl: "/metro.svg", iconSize: [29, 15] });
	const tramIcon = new L.Icon({ iconUrl: "/tram.svg", iconSize: [29, 15] });

	let mapPromise = $state(fetchMap());
	let gamePromise = $state(fetchGame());
	let isChecking = $state(false);
	let results: CheckResponse | null = $state(null);

	let currentIndex = $state(0);

	let map: L.Map | null = $state(null);
	let playerMarker: L.Marker | null = $state(null);
	let solutionMarker: L.Marker | null = $state(null);
	let solutionLine: L.Polyline | null = $state(null);

	onMount(() => {
		window.addEventListener("beforeunload", async (e) => {
			const gameData = await gamePromise;
			if (gameData.stopNames.length > currentIndex + 1) {
				e.preventDefault();
			}
		});
	});

	async function fetchMap(): Promise<MapData> {
		return fetch("/api/map?" + $page.url.searchParams).then((r) => r.json());
	}

	async function fetchGame(): Promise<ClientGameData> {
		return fetch("/api/game?" + $page.url.searchParams).then((r) => r.json());
	}

	mapPromise.then((mapData) => {
		map = L.map("map").setView(center, zoom);

		L.tileLayer(`https://basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png`, {
			attribution:
				'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
			maxZoom: 20,
		}).addTo(map);

		// we know map isn't null
		mapData.lines.forEach(([feature, color]) => {
			L.geoJSON(feature, { style: { color } }).addTo(map!);
		});

		const icon = $page.url.searchParams.get("stops_type") === "tram" ? tramIcon : metroIcon;
		mapData.stops.forEach((coords) => {
			const marker = L.marker(coords, { icon }).addTo(map!);
			marker.on("click", (e) => setMarker(e.latlng));
		});

		map.on("click", (e) => setMarker(e.latlng));
		map.on("keydown", (e) => {
			if (e.originalEvent.key === " ") {
				checkLocation();
			}
		});
	});

	function setMarker(pos: L.LatLng) {
		if (map && !results) {
			playerMarker = (playerMarker ?? L.marker(pos).addTo(map)).setLatLng(pos);
		}
	}

	async function checkLocation() {
		if (!playerMarker || results || isChecking) return;
		isChecking = true;

		const gameData = await gamePromise;

		const checkData: CheckData = {
			gameId: gameData.gameId,
			stopName: gameData.stopNames[currentIndex],
			latlng: [playerMarker.getLatLng().lat, playerMarker.getLatLng().lng],
		};

		const response = await fetch("/api/check", {
			method: "POST",
			body: JSON.stringify(checkData),
		});

		if (response.ok && map) {
			const res: CheckResponse = await response.json();
			results = res;

			solutionLine = L.polyline([checkData.latlng, res.solution], {
				color: "black",
				weight: 6,
			}).addTo(map);

			map.flyToBounds(solutionLine.getBounds(), { duration: 1 });

			solutionMarker = L.marker(res.solution)
				.bindPopup(gameData.stopNames[currentIndex])
				.addTo(map)
				.openPopup();
		} else {
			alert(`une erreur est survenue: ${response.status} ${await response.text()}`);
		}

		isChecking = false;
	}

	async function restartGame() {
		if (!map) return;
		const gameData = await gamePromise;

		playerMarker?.removeFrom(map);
		playerMarker = null;

		solutionLine?.removeFrom(map);
		solutionLine = null;

		solutionMarker?.removeFrom(map);
		solutionMarker = null;

		map.setView(center, zoom);
		results = null;

		if (gameData.stopNames.length <= currentIndex + 1) {
			window.location.href = "/results?" + new URLSearchParams({ gameId: gameData.gameId });
		} else {
			currentIndex++;
		}
	}
</script>

<svelte:head>
	<title>TCL-Guessr</title>
</svelte:head>

{#await Promise.all([mapPromise, gamePromise])}
	<h1>Chargement...</h1>

	<div><button disabled>Chargement...</button></div>
{:then [, gameData]}
	<h1>{gameData.stopNames[currentIndex]}</h1>

	<div>
		{#if results === null}
			<button onclick={checkLocation} disabled={!playerMarker || isChecking}>VÉRIFIER</button>
		{:else}
			<button onclick={restartGame}>SUIVANT</button>
		{/if}
	</div>
{/await}

<span class="results" hidden={!results}>
	{results?.score} points! Vous etiez à {results?.distance}m.
</span>

<div id="map"></div>

<style>
	:global(body),
	:global(html) {
		margin: 0;
		height: 100%;
	}

	:global(.contents) {
		max-width: 100% !important;
	}

	button {
		padding: 10px;
	}

	.results {
		color: green;
		font-size: 20px;
	}

	#map {
		flex-grow: 1;
		width: 100%;
	}
</style>
