<script lang="ts">
	import { page } from '$app/state';
	import '$lib/styles/jeu.css';
	import GameMap from '$lib/components/GameMap.svelte';
	import CarteRegions, { type MarqueurCarte } from '$lib/components/CarteRegions.svelte';
	import PhotoPanel from '$lib/components/PhotoPanel.svelte';
	import ResultPanel from '$lib/components/ResultPanel.svelte';
	import Seo from '$lib/components/Seo.svelte';
	import TimerRing from '$lib/components/TimerRing.svelte';
	import { t } from '$lib/i18n/store.svelte';
	import { compte } from '$lib/motion/countup';
	import { reveal } from '$lib/motion/reveal';
	import { syncopatedDelay } from '$lib/motion/tokens';
	import { fonduMesure, monteeMesure } from '$lib/motion/transitions';
	import { partager } from '$lib/partage';
	import { defi } from '$lib/stores/defi.svelte';
	import { ajouterPartie } from '$lib/stores/history.svelte';
	import type { Coordonnees, GuessResult, RoundPlace } from '$lib/types';

	let { data } = $props();
	const seed = $derived(data.seed);
	const rounds = $derived(data.rounds);

	const timerActif = $derived(page.url.searchParams.get('timer') === '1');

	let place = $state<RoundPlace | null>(null);
	let guess = $state<Coordonnees | null>(null);
	let result = $state<GuessResult | null>(null);
	let indicesMontres = $state<string[]>([]);
	let erreur = $state(false);
	let enCours = $state(false); // requête guess en vol

	let timerRef: ReturnType<typeof TimerRing> | undefined = $state();
	let bilan = $state(false); // écran final du défi
	let lienCopie = $state(false); // retour visuel du bouton « Copier le lien »

	const mancheCourante = $derived(defi.manches.length + 1);
	const defiTermine = $derived(defi.termine);
	// Marqueurs du récap SVG : position réelle (vert) + guess du joueur (corail),
	// numérotés par manche ; une manche passée (guess null) n'a que le point réel.
	const marqueursRecap = $derived(
		defi.manches.flatMap((m, i): MarqueurCarte[] => {
			const reel: MarqueurCarte = {
				lat: m.result.coordonnees.lat,
				lon: m.result.coordonnees.lon,
				type: 'reel',
				label: m.result.nom,
				paire: i + 1
			};
			if (!m.guess) return [reel];
			return [
				reel,
				{ lat: m.guess.lat, lon: m.guess.lon, type: 'guess', label: m.result.nom, paire: i + 1 }
			];
		})
	);

	// Les 5 lieux sont préchargés par le load : la manche courante suit l'état du store.
	function chargerRound(): void {
		guess = null;
		result = null;
		indicesMontres = [];
		place = rounds[defi.manches.length] ?? null;
		erreur = !place;
	}

	async function soumettre(skip: boolean): Promise<void> {
		if (!place || result || enCours) return;
		if (!skip && !guess) return;
		enCours = true;
		try {
			const body: Record<string, unknown> = { id: place.id };
			if (skip) {
				body.skip = true;
			} else {
				body.lat = guess!.lat;
				body.lon = guess!.lon;
				body.hintsUsed = indicesMontres.length;
				if (timerActif && timerRef) body.timeMs = timerRef.tempsMs();
			}
			const res = await fetch('/api/guess', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify(body)
			});
			if (!res.ok) throw new Error(String(res.status));
			result = (await res.json()) as GuessResult;
			defi.ajouter({ place, guess: skip ? null : guess, result });
			if (defi.termine) {
				ajouterPartie({
					mode: 'defi',
					region: 'TOUTES',
					scoreTotal: defi.total,
					roundsCount: defi.manches.length,
					rounds: defi.manches.map((m) => ({
						nom: m.result.nom,
						commune: m.result.commune,
						region: m.result.region,
						distanceKm: m.result.distanceKm,
						score: m.result.score
					}))
				});
			}
		} catch {
			erreur = true;
		} finally {
			enCours = false;
		}
	}

	function montrerIndice(): void {
		if (!place) return;
		const prochain = place.indices.find((i) => !indicesMontres.includes(i.texte));
		if (prochain) indicesMontres = [...indicesMontres, prochain.texte];
	}

	function suivant(): void {
		chargerRound();
	}

	function montrerBilan(): void {
		bilan = true;
	}

	function rejouerDefi(): void {
		defi.reinitialiser();
		bilan = false;
		lienCopie = false;
		chargerRound();
	}

	/** Copie l'URL du défi dans le presse-papiers (repli : champ temporaire + sélection). */
	async function copierLien(): Promise<void> {
		const url = `${window.location.origin}/defi/${seed}`;
		try {
			await navigator.clipboard.writeText(url);
		} catch {
			// Contexte non sécurisé (HTTP) ou navigateur sans Clipboard API.
			const champ = document.createElement('textarea');
			champ.value = url;
			document.body.appendChild(champ);
			champ.select();
			document.execCommand('copy');
			champ.remove();
		}
		lienCopie = true;
		setTimeout(() => (lienCopie = false), 2500);
	}

	// (Ré)initialise le défi au montage et à chaque changement de seed :
	// en navigation client d'un /defi/<seed> vers un autre, le composant est conservé.
	let seedJouee = '';
	$effect(() => {
		if (seed === seedJouee) return;
		seedJouee = seed;
		defi.reinitialiser();
		bilan = false;
		lienCopie = false;
		chargerRound();
	});
</script>

<Seo
	title="{t().modes.defi} — seed {seed}"
	description={t().modes.defiDesc}
	path="/defi/{seed}"
/>

<div class="jeu">
	{#if bilan}
		<div class="bilan" in:monteeMesure>
			<h1>{t().defi.bilan}</h1>
			<p class="seed-discret">{t().defi.titreSeed} <strong>{seed}</strong></p>
			<div class="filet-drapeau" aria-hidden="true">
				<span class="seg seg-vert"></span><span class="seg seg-corail"></span><span class="seg seg-bleu"></span>
			</div>
			<p class="total">
				<span class="valeur" aria-hidden="true" use:compte={defi.total}>{defi.total}</span>
				<span class="sr-only">{defi.total}</span>
				<span class="unite">{t().defi.scoreTotal}</span>
			</p>
			<div class="bilan-carte">
				<CarteRegions selectionnable={false} marqueurs={marqueursRecap} />
			</div>
			<ol class="recap">
				{#each defi.manches as m, i (m.place.id)}
					<li data-reveal style:--d="{syncopatedDelay(i)}ms" use:reveal>
						<span class="r-nom">{i + 1}. {m.result.nom}</span>
						<span class="r-region">{t().regions[m.result.region]}</span>
						<span class="r-score">{m.result.score} {t().resultat.points}</span>
					</li>
				{/each}
			</ol>
			<div class="bilan-actions">
				<button type="button" class="btn btn-primaire" onclick={partager}>
					{t().defi.partager}
				</button>
				<button type="button" class="btn btn-secondaire" onclick={() => void copierLien()}>
					{lienCopie ? t().defi.lienCopie : t().defi.copierLien}
				</button>
				<button type="button" class="btn btn-secondaire" onclick={rejouerDefi}>
					{t().resultat.rejouer}
				</button>
				<a href="/" class="btn btn-tertiaire">{t().resultat.retourAccueil}</a>
			</div>
			<p class="legende">
				<span class="puce joueur" aria-hidden="true">●</span> {t().defi.taPosition} —
				<span class="puce reelle" aria-hidden="true">●</span> {t().defi.positionReelle}
			</p>
		</div>
	{:else if erreur}
		<div class="etat" in:fonduMesure>
			<p>{t().jeu.erreur}</p>
			<button type="button" class="btn btn-secondaire" onclick={chargerRound}>
				{t().jeu.reessayer}
			</button>
		</div>
	{:else if place}
		<div class="split" class:resultat={!!result} in:monteeMesure>
			<section class="panneau-photo">
				{#if result}
					<div class="resultat-scroll">
						<ResultPanel
							{result}
							creditPhoto={place.photo?.credit}
							labelBouton={defiTermine ? t().resultat.voirBilan : t().resultat.suivant}
							onsuivant={defiTermine ? montrerBilan : suivant}
						/>
					</div>
				{:else if place.photo}
					<PhotoPanel photo={place.photo} />
				{/if}
				{#if !result && indicesMontres.length > 0}
					<aside class="indices" aria-live="polite">
						{#each indicesMontres as texte, i (texte)}
							<p class="indice"><strong>{t().jeu.indice} {i + 1}</strong> — {texte}</p>
						{/each}
					</aside>
				{/if}
			</section>

			<section class="panneau-carte">
				<GameMap
					bind:guess
					real={result ? result.coordonnees : null}
					distanceKm={result ? result.distanceKm : null}
					interactive={!result}
					onvalidate={() => void soumettre(false)}
				/>
				<div class="statut">
					{#if !defiTermine}
						<span class="badge-manche">{t().jeu.round} {Math.min(mancheCourante, 5)} {t().jeu.sur} 5</span>
					{/if}
					<span class="badge-seed">{t().defi.titreSeed} {seed}</span>
					{#if timerActif && !result}
						<span class="badge-timer">
							<TimerRing bind:this={timerRef} actif={!result} />
						</span>
					{/if}
				</div>
				{#if !result && !guess}
					<p class="aide-carte">{t().jeu.poserMarqueur}</p>
				{/if}
			</section>
		</div>

		{#if !result}
			<div class="barre-actions">
				<div class="actions-secondaires">
					<button
						type="button"
						class="btn btn-tertiaire"
						onclick={montrerIndice}
						disabled={indicesMontres.length >= place.indices.length}
					>
						{t().jeu.indice}
						<span class="penalite">{t().jeu.indicePenalite}</span>
					</button>
					<button type="button" class="btn btn-secondaire" onclick={() => void soumettre(true)}>
						{t().jeu.passer}
					</button>
				</div>
				<button
					type="button"
					class="btn btn-primaire valider"
					disabled={!guess || enCours}
					onclick={() => void soumettre(false)}
				>
					{t().jeu.valider}
				</button>
			</div>
		{/if}
	{:else}
		<div class="etat" in:fonduMesure><p>{t().jeu.charger}</p></div>
	{/if}
</div>

