<script>
	import { onMount } from 'svelte';
	import { env } from '$env/dynamic/public';

	// Svelte 5 Runes for Core Reactivity
	let photos = $state([]);
	let nextToken = $state(null);
	let loading = $state(false);
	let hasMore = $state(true);
	let activePhoto = $state(null);
	let observerTarget;

	// Lightbox Control Flags
	let isZoomed = $state(false);
	let lightboxContainer = $state(null);
	let imageMatrixWrapper = $state(null); // Explicit ref to bypass reactive state lag
	let isDragging = false; // Internal mutable flag (not a reactive rune)
	
	// Coordinate Vector Matrix (using raw numbers to bypass Svelte state loops on fast cycles)
	let currentX = 0;
	let currentY = 0;
	let startX = 0;
	let startY = 0;
	let ticking = false;

	// Touch tracking coordinates for layout pagination swipes
	let touchStartX = 0;
	let touchEndX = 0;

	// Compute active index reactively
	let activeIndex = $derived(
		activePhoto ? photos.findIndex(p => p.id === activePhoto.id) : -1
	);

	async function loadPhotos() {
		if (loading || !hasMore) return;
		loading = true;

		const url = new URL('/api/photos', window.location.origin);
		if (nextToken) url.searchParams.set('next', nextToken);

		try {
			const res = await fetch(url);
			const data = await res.json();
			
			if (data && Array.isArray(data.photos)) {
				photos = [...photos, ...data.photos];
				nextToken = data.nextContinuationToken;
				hasMore = !!nextToken;
			}
		} catch (err) {
			console.error('Failed to load photos:', err);
		} finally {
			loading = false;
		}
	}

	onMount(() => {
		loadPhotos();

		const observer = new IntersectionObserver((entries) => {
			if (entries[0].isIntersecting && hasMore) {
				loadPhotos();
			}
		}, { rootMargin: '300px' });

		if (observerTarget) observer.observe(observerTarget);

		return () => observer.disconnect();
	});

	function changeActivePhoto(photo) {
		activePhoto = photo;
		resetZoom();
	}

	function resetZoom() {
		isZoomed = false; 
		isDragging = false;
		currentX = 0;
		currentY = 0;
		if (imageMatrixWrapper) {
			imageMatrixWrapper.style.transform = `translate3d(0px, 0px, 0px)`;
		}
	}

	function nextPhoto() {
		if (activeIndex !== -1 && activeIndex < photos.length - 1) {
			changeActivePhoto(photos[activeIndex + 1]);
			if (activeIndex >= photos.length - 4) loadPhotos();
		}
	}

	function prevPhoto() {
		if (activeIndex > 0) {
			changeActivePhoto(photos[activeIndex - 1]);
		}
	}

	function handleKeyDown(e) {
		if (!activePhoto) return;
		
		if (e.key === 'Escape') {
			e.preventDefault();
			activePhoto = null;
		}
		if (e.key === 'ArrowRight' && !isZoomed) {
			e.preventDefault();
			nextPhoto();
		}
		if (e.key === 'ArrowLeft' && !isZoomed) {
			e.preventDefault();
			prevPhoto();
		}
	}

	function toggleFullscreen() {
		if (!lightboxContainer) return;
		if (!document.fullscreenElement) {
			lightboxContainer.requestFullscreen().catch(err => console.error(err));
		} else {
			document.exitFullscreen();
		}
	}

	// HIGH PERFORMANCE NATIVE INTERFACE PIPELINE (60FPS Engine)
	function pointerDown(clientX, clientY, target) {
		if (!isZoomed) return;
		if (target.tagName === 'BUTTON' || target.closest('button')) return;

		isDragging = true;
		startX = clientX - currentX;
		startY = clientY - currentY;
	}

	function pointerMove(clientX, clientY) {
		if (!isDragging) return;
		
		currentX = clientX - startX;
		currentY = clientY - startY;

		// requestAnimationFrame schedules updates with the browser screen refresh cycle
		if (!ticking) {
			window.requestAnimationFrame(() => {
				if (imageMatrixWrapper) {
					imageMatrixWrapper.style.transform = `translate3d(${currentX}px, ${currentY}px, 0px)`;
				}
				ticking = false;
			});
			ticking = true;
		}
	}

	function pointerUp() {
		isDragging = false;
	}

	// Desktop Mouse Interface Subscriptions
	function handleMouseDown(e) { pointerDown(e.clientX, e.clientY, e.target); }
	function handleMouseMove(e) { 
		if(isDragging) e.preventDefault();
		pointerMove(e.clientX, e.clientY); 
	}

	// Mobile Touch Interface Subscriptions
	function handleTouchStart(e) {
		if (isZoomed) {
			const touch = e.touches[0];
			pointerDown(touch.clientX, touch.clientY, e.target);
		} else {
			touchStartX = e.changedTouches[0].screenX;
		}
	}

	function handleTouchMove(e) {
		if (isZoomed && e.touches.length === 1) {
			e.preventDefault(); // Kill native momentum bouncing
			const touch = e.touches[0];
			pointerMove(touch.clientX, touch.clientY);
		}
	}

	function handleTouchEnd(e) {
		if (isZoomed) {
			pointerUp();
		} else {
			touchEndX = e.changedTouches[0].screenX;
			const threshold = 50;
			if (touchStartX - touchEndX > threshold) nextPhoto();
			else if (touchEndX - touchStartX > threshold) prevPhoto();
		}
	}
</script>

<svelte:window onkeydown={handleKeyDown} />

<div class="min-h-screen bg-zinc-950 text-zinc-100">
	<header class="sticky top-0 z-10 border-b border-zinc-800 bg-zinc-950/80 backdrop-blur-md px-6 py-4 flex justify-between items-center">
		<h1 class="text-xl font-bold tracking-wide">{env.PUBLIC_GALLERY_NAME}</h1>
		<span class="text-xs text-zinc-500 uppercase tracking-widest bg-zinc-900 border border-zinc-800 px-3 py-1 rounded-full">Secure Session</span>
	</header>

	<main class="p-4 sm:p-6 lg:p-8 max-w-[1600px] mx-auto">
		<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3 sm:gap-4">
			{#each photos as photo (photo.id)}
				<button 
					class="group relative aspect-square overflow-hidden rounded-xl bg-zinc-900 border border-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-500"
					onclick={() => changeActivePhoto(photo)}
				>
					<img 
						src={photo.thumbUrl} 
						alt="Gallery item" 
						loading="lazy"
						class="h-full w-full object-cover object-center transition-transform duration-300 group-hover:scale-105"
					/>
					<div class="absolute inset-0 bg-black/20 opacity-0 transition-opacity group-hover:opacity-100"></div>
				</button>
			{/each}
		</div>

		<div bind:this={observerTarget} class="w-full flex justify-center py-12">
			{#if loading}
				<div class="h-6 w-6 animate-spin rounded-full border-2 border-zinc-500 border-t-transparent"></div>
			{/if}
		</div>
	</main>
</div>

{#if activePhoto}
	<div 
		bind:this={lightboxContainer}
		class="fixed inset-0 z-50 flex flex-col items-center justify-between bg-black/95 p-4 select-none touch-none"
		role="dialog"
		aria-modal="true"
		tabindex="-1"
		onclick={(e) => { if (e.target === e.currentTarget) activePhoto = null; }}
		onkeydown={handleKeyDown}
		ontouchstart={handleTouchStart}
		ontouchmove={handleTouchMove}
		ontouchend={handleTouchEnd}
	>
		<div class="w-full flex justify-between items-center z-50 p-2 pointer-events-none">
			<div class="text-sm font-medium tracking-wider bg-zinc-900/80 px-4 py-2 rounded-full border border-zinc-800/60 text-zinc-300 pointer-events-auto">
				{activeIndex + 1} <span class="text-zinc-600 px-0.5">/</span> {photos.length}
			</div>

			<div class="flex items-center gap-2 pointer-events-auto">
				<button 
					class="rounded-full bg-zinc-900/80 p-2.5 text-zinc-300 hover:text-white transition hover:bg-zinc-800"
					title={isZoomed ? "Reset Zoom" : "Zoom to Full Resolution"}
					onclick={() => { if(isZoomed) { resetZoom(); } else { isZoomed = true; } }}
				>
					{#if isZoomed}
						<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
							<path stroke-linecap="round" stroke-linejoin="round" d="M9 9V4.5M9 9H4.5M9 9L3.75 3.75M15 9V4.5M15 9h4.5M15 9l5.25-5.25M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 15v4.5M15 15h4.5M15 15l5.25 5.25" />
						</svg>
					{:else}
						<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
							<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75v4.5m0-4.5h-4.5m4.5 0L15 9m5.25 11.25v-4.5m0 4.5h-4.5m4.5 0L15 15" />
						</svg>
					{/if}
				</button>

				<button 
					class="rounded-full bg-zinc-900/80 p-2.5 text-zinc-300 hover:text-white transition hover:bg-zinc-800"
					title="Toggle Fullscreen"
					onclick={toggleFullscreen}
				>
					<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
						<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9h16.5m-16.5 6h16.5" />
					</svg>
				</button>

				<a 
					href="/api/download?id={activePhoto.id}"
					class="rounded-full bg-zinc-900/80 p-2.5 text-zinc-300 hover:text-white transition hover:bg-zinc-800"
					title="Download Original"
				>
					<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
						<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
					</svg>
				</a>
				
				<button 
					class="rounded-full bg-zinc-900/80 p-2.5 text-zinc-300 hover:text-white transition hover:bg-zinc-800"
					title="Close"
					onclick={() => activePhoto = null}
				>
					<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
						<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
					</svg>
				</button>
			</div>
		</div>

		<div 
			class="relative w-full flex-1 flex items-center justify-center overflow-hidden my-4 selection:bg-transparent"
			class:cursor-grab={isZoomed && !isDragging}
			class:cursor-grabbing={isZoomed && isDragging}
			onmousedown={handleMouseDown}
			onmousemove={handleMouseMove}
			onmouseup={pointerUp}
			onmouseleave={pointerUp}
		>
			{#if !isZoomed}
				<button 
					type="button" 
					class="absolute left-0 top-0 bottom-0 w-[10%] z-30 cursor-w-resize focus:outline-none flex items-center justify-start pl-4 group"
					onclick={prevPhoto}
					aria-label="Previous photo"
					disabled={activeIndex === 0}
				>
					{#if activeIndex > 0}
						<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-8 h-8 text-zinc-500 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
							<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
						</svg>
					{/if}
				</button>

				<button 
					type="button" 
					class="absolute right-0 top-0 bottom-0 w-[10%] z-30 cursor-e-resize focus:outline-none flex items-center justify-end pr-4 group"
					onclick={nextPhoto}
					aria-label="Next photo"
					disabled={activeIndex === photos.length - 1}
				>
					{#if activeIndex < photos.length - 1}
						<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-8 h-8 text-zinc-500 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
							<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
						</svg>
					{/if}
				</button>
			{/if}

			<div 
				bind:this={imageMatrixWrapper}
				class="max-w-full max-h-full flex items-center justify-center p-2 ease-out will-change-transform"
			>
				<img 
					src={activePhoto.fullUrl} 
					alt="Gallery Content Visual" 
					draggable="false"
					class="max-h-[70vh] max-w-full rounded shadow-2xl select-none pointer-events-none transition-transform duration-200 ease-out will-change-transform"
					style="transform: scale({isZoomed ? 3.8 : 1});"
				/>
			</div>
		</div>

		<div class="w-full max-w-4xl mx-auto z-40 bg-zinc-950/90 border border-zinc-900 px-4 py-3 rounded-xl backdrop-blur-md pointer-events-auto">
			<div class="flex items-center gap-2 overflow-x-auto overflow-y-hidden py-1 px-0.5 scrollbar-thin scrollbar-thumb-zinc-800 scroll-smooth">
				{#each photos as thumbPhoto, idx}
					<button 
						type="button"
						class="relative flex-shrink-0 w-16 h-12 rounded-md overflow-hidden border transition-all duration-200 focus:outline-none
						{idx === activeIndex ? 'border-blue-500 scale-105 ring-2 ring-blue-500/20 opacity-100' : 'border-zinc-800/80 opacity-40 hover:opacity-80'}"
						onclick={() => changeActivePhoto(thumbPhoto)}
					>
						<img 
							src={thumbPhoto.thumbUrl} 
							alt="Ribbon Navigation Frame" 
							class="w-full h-full object-cover"
						/>
					</button>
				{/each}
			</div>
		</div>
	</div>
{/if}