<script lang="ts">
	import { Button } from '$lib/components/ui/button';
	import * as Card from '$lib/components/ui/card';
	import FileDropZone from '$lib/components/ui/file-drop-zone.svelte';
	import * as AlertDialog from '$lib/components/ui/alert-dialog';
	import { apiFetch } from '$lib/utils/api';

	// =============== Image Upload ===============
	let uploading = $state(false);
	let uploadFiles = $state<File[]>([]);
	let filePreviews = $state<{ file: File; preview: string }[]>([]);
	let uploadProgress = $state(0);
	let uploadStatus = $state<Record<string, string>>({});
	let uploadResults = $state<{ name: string; url?: string; error?: string }[]>([]);

	const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB

	function formatFileSize(bytes: number): string {
		if (bytes >= 1024 * 1024) {
			return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
		}
		return (bytes / 1024).toFixed(1) + ' KB';
	}

	function isFileTooBig(file: File): boolean {
		return file.size > MAX_FILE_SIZE;
	}

	const hasOversizedFiles = $derived(uploadFiles.some(isFileTooBig));

	function isHeicFile(file: File): boolean {
		const name = file.name.toLowerCase();
		return name.endsWith('.heic') || name.endsWith('.heif');
	}

	/**
	 * Check if the browser can natively decode a HEIC file.
	 * Safari (macOS/iOS) and Chrome-on-Android have native HEIC support,
	 * which preserves HDR metadata and color profiles better than any conversion.
	 */
	function supportsNativeHeicDecode(file: File): Promise<boolean> {
		return new Promise((resolve) => {
			const img = new Image();
			const url = URL.createObjectURL(file);
			img.onload = () => {
				URL.revokeObjectURL(url);
				resolve(true);
			};
			img.onerror = () => {
				URL.revokeObjectURL(url);
				resolve(false);
			};
			img.src = url;
		});
	}

	async function convertHeicToPng(file: File): Promise<File> {
		const heic2any = (await import('heic2any')).default;
		const result = await heic2any({ blob: file, toType: 'image/png' });
		const blob = Array.isArray(result) ? result[0] : result;
		const pngName = file.name.replace(/\.(heic|heif)$/i, '.png');
		return new File([blob], pngName, { type: 'image/png' });
	}

	async function handleFilesDropped(files: File[]) {
		const converted = await Promise.all(
			files.map(async (file) => {
				if (!isHeicFile(file)) return file;

				// Try native browser HEIC decode first (Safari, Chrome w/ HEVC)
				// This preserves HDR metadata & color profiles at full quality
				try {
					const nativeOk = await supportsNativeHeicDecode(file);
					if (nativeOk) return file;
				} catch {
					// Fall through to heic2any
				}

				// Fallback: convert to PNG via heic2any (libheif WASM)
				// PNG is lossless, libheif handles ICC profiles correctly,
				// and the downstream canvas pipeline is 8-bit anyway.
				try {
					return await convertHeicToPng(file);
				} catch (e) {
					console.error('HEIC conversion failed for', file.name, e);
					return file;
				}
			})
		);
		uploadFiles = converted;
		generatePreviews(converted);
	}

	/** Generate scaled previews for each file */
	function generatePreviews(files: File[]) {
		filePreviews = [];
		files.forEach((file) => {
			const reader = new FileReader();
			reader.onload = (e) => {
				const img = new Image();
				img.onload = () => {
					const canvas = document.createElement('canvas');
					const scale = 150 / Math.max(img.width, img.height);
					canvas.width = img.width * scale;
					canvas.height = img.height * scale;
					const ctx = canvas.getContext('2d');
					if (ctx) {
						ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
						const preview = canvas.toDataURL('image/jpeg', 0.8);
						filePreviews = [...filePreviews, { file, preview }];
					}
				};
				img.src = e.target?.result as string;
			};
			reader.readAsDataURL(file);
		});
	}

	/** Load a File or Blob into an HTMLImageElement. */
	function loadImage(src: File | Blob): Promise<HTMLImageElement> {
		return new Promise((resolve, reject) => {
			const img = new Image();
			img.onload = () => resolve(img);
			img.onerror = () => reject(new Error('Image load failed'));
			img.src = URL.createObjectURL(src);
		});
	}

	/** Encode ImageData to AVIF via Web Worker. */
	function encodeAvif(imageData: ImageData, quality: number): Promise<Blob> {
		return new Promise((resolve, reject) => {
			const worker = new Worker(new URL('$lib/workers/avif-encoder.ts', import.meta.url), {
				type: 'module'
			});
			worker.onmessage = (
				e: MessageEvent<{ encoded: ArrayBuffer; format: string; error?: string }>
			) => {
				worker.terminate();
				if (e.data.error) return reject(new Error(e.data.error));
				resolve(new Blob([e.data.encoded], { type: 'image/avif' }));
			};
			worker.onerror = (err) => {
				worker.terminate();
				reject(err);
			};
			worker.postMessage({ imageData, quality });
		});
	}

	/** Encode ImageData to WebP via Web Worker. */
	function encodeWebp(imageData: ImageData, quality: number): Promise<Blob> {
		return new Promise((resolve, reject) => {
			const worker = new Worker(new URL('$lib/workers/webp-encoder.ts', import.meta.url), {
				type: 'module'
			});
			worker.onmessage = (
				e: MessageEvent<{ encoded: ArrayBuffer; format: string; error?: string }>
			) => {
				worker.terminate();
				if (e.data.error) return reject(new Error(e.data.error));
				resolve(new Blob([e.data.encoded], { type: 'image/webp' }));
			};
			worker.onerror = (err) => {
				worker.terminate();
				reject(err);
			};
			worker.postMessage({ imageData, quality });
		});
	}

	/** Encode ImageData to JPEG via Web Worker. */
	function encodeJpeg(imageData: ImageData, quality: number): Promise<Blob> {
		return new Promise((resolve, reject) => {
			const worker = new Worker(new URL('$lib/workers/jpeg-encoder.ts', import.meta.url), {
				type: 'module'
			});
			worker.onmessage = (
				e: MessageEvent<{ encoded: ArrayBuffer; format: string; error?: string }>
			) => {
				worker.terminate();
				if (e.data.error) return reject(new Error(e.data.error));
				resolve(new Blob([e.data.encoded], { type: 'image/jpeg' }));
			};
			worker.onerror = (err) => {
				worker.terminate();
				reject(err);
			};
			worker.postMessage({ imageData, quality });
		});
	}

	/** Encode ImageData to JPEG XL via Web Worker. Returns null if unavailable. */
	function encodeJxl(imageData: ImageData, quality: number): Promise<Blob | null> {
		return new Promise((resolve) => {
			const worker = new Worker(new URL('$lib/workers/jxl-encoder.ts', import.meta.url), {
				type: 'module'
			});
			const timeout = setTimeout(() => {
				worker.terminate();
				resolve(null);
			}, 10000);
			worker.onmessage = (
				e: MessageEvent<{ encoded: ArrayBuffer; format: string; error?: string }>
			) => {
				clearTimeout(timeout);
				worker.terminate();
				if (e.data.error) return resolve(null);
				resolve(new Blob([e.data.encoded], { type: 'image/jxl' }));
			};
			worker.onerror = () => {
				clearTimeout(timeout);
				worker.terminate();
				resolve(null);
			};
			worker.postMessage({ imageData, quality });
		});
	}

	/** Create a 250×250 center-cropped thumbnail canvas using multi-pass downsampling. */
	function createThumbnailCanvas(img: HTMLImageElement): HTMLCanvasElement {
		const targetSize = 250;
		let srcWidth = img.width;
		let srcHeight = img.height;
		let sourceCanvas: HTMLCanvasElement | null = null;

		const SCALE_FACTOR = 0.2;
		const STOP_THRESHOLD = targetSize * 1.1;

		while (Math.min(srcWidth, srcHeight) > STOP_THRESHOLD) {
			const scale = Math.max(SCALE_FACTOR, targetSize / Math.min(srcWidth, srcHeight));
			const newWidth = Math.round(srcWidth * scale);
			const newHeight = Math.round(srcHeight * scale);

			const nextCanvas = document.createElement('canvas');
			nextCanvas.width = newWidth;
			nextCanvas.height = newHeight;
			const nextCtx = nextCanvas.getContext('2d')!;
			nextCtx.imageSmoothingEnabled = true;
			nextCtx.imageSmoothingQuality = 'high';

			if (sourceCanvas === null) {
				nextCtx.drawImage(img, 0, 0, newWidth, newHeight);
			} else {
				nextCtx.drawImage(sourceCanvas, 0, 0, newWidth, newHeight);
			}

			sourceCanvas = nextCanvas;
			srcWidth = newWidth;
			srcHeight = newHeight;
		}

		// Final crop to exact 250×250 square from center
		const finalCanvas = document.createElement('canvas');
		finalCanvas.width = targetSize;
		finalCanvas.height = targetSize;
		const finalCtx = finalCanvas.getContext('2d')!;
		finalCtx.imageSmoothingEnabled = true;
		finalCtx.imageSmoothingQuality = 'high';

		if (sourceCanvas === null) {
			const offsetX = (img.width - targetSize) / 2;
			const offsetY = (img.height - targetSize) / 2;
			finalCtx.drawImage(
				img,
				offsetX,
				offsetY,
				targetSize,
				targetSize,
				0,
				0,
				targetSize,
				targetSize
			);
		} else {
			const offsetX = (srcWidth - targetSize) / 2;
			const offsetY = (srcHeight - targetSize) / 2;
			finalCtx.drawImage(
				sourceCanvas,
				offsetX,
				offsetY,
				targetSize,
				targetSize,
				0,
				0,
				targetSize,
				targetSize
			);
		}

		return finalCanvas;
	}

	/** Resize to max 1500px on the short side, only scale down, never up. Returns canvas. */
	function createFullCanvas(img: HTMLImageElement): HTMLCanvasElement {
		let { width, height } = img;
		const maxShortSide = 1500;

		const shortSide = Math.min(width, height);
		if (shortSide > maxShortSide) {
			if (width < height) {
				const scale = maxShortSide / width;
				width = maxShortSide;
				height = Math.round(height * scale);
			} else {
				const scale = maxShortSide / height;
				height = maxShortSide;
				width = Math.round(width * scale);
			}
		}

		const canvas = document.createElement('canvas');
		canvas.width = width;
		canvas.height = height;
		const ctx = canvas.getContext('2d')!;
		ctx.imageSmoothingEnabled = true;
		ctx.imageSmoothingQuality = 'high';
		ctx.drawImage(img, 0, 0, width, height);
		return canvas;
	}

	/** Generate all thumbnail variants (AVIF, WebP, JPEG) from a single file. */
	async function generateThumbnailVariants(file: File): Promise<{
		avif: Blob;
		webp: Blob;
		jpg: Blob;
	}> {
		const img = await loadImage(file);
		const canvas = createThumbnailCanvas(img);
		const ctx = canvas.getContext('2d')!;
		const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

		const [avif, webp, jpg] = await Promise.all([
			encodeAvif(imageData, 80),
			encodeWebp(imageData, 80),
			encodeJpeg(imageData, 85)
		]);

		return { avif, webp, jpg };
	}

	/** Generate all full-size variants (AVIF, WebP, JPEG, optional JXL) from a single file. */
	async function generateFullVariants(file: File): Promise<{
		avif: Blob;
		webp: Blob;
		jpg: Blob;
		jxl: Blob | null;
	}> {
		const img = await loadImage(file);
		const canvas = createFullCanvas(img);
		const ctx = canvas.getContext('2d')!;
		const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

		const [avif, webp, jpg, jxl] = await Promise.all([
			encodeAvif(imageData, 72),
			encodeWebp(imageData, 80),
			encodeJpeg(imageData, 85),
			encodeJxl(imageData, 75)
		]);

		return { avif, webp, jpg, jxl };
	}

	let availableTags = $state<string[]>([]);
	let _loadingTags = $state(true);
	let isMobile = $state(false);

	async function fetchTags() {
		try {
			const response = await fetch('/api/portfolio/tags');
			if (response.ok) {
				const data = await response.json();
				availableTags = data.map((t: { name: string }) => t.name);
			}
		} catch (e) {
			console.error('Failed to fetch tags:', e);
		} finally {
			_loadingTags = false;
		}
	}

	// Check for mobile on mount
	if (typeof window !== 'undefined') {
		isMobile = window.matchMedia('(pointer: coarse)').matches;
	}

	fetchTags();

	let tags = $state<string[]>([]);
	let input = $state('');
	let selectedSuggestionIndex = $state(-1);
	let inputRef = $state<HTMLInputElement | undefined>(undefined);
	let showConfirmUploadAlert = $state(false);

	const suggestions = $derived.by(() => {
		const q = input.trim().toLowerCase();
		if (!q) return [];

		return availableTags
			.map((t) => t.toLowerCase())
			.filter((t) => t.startsWith(q) && !tags.includes(t))
			.slice(0, 4);
	});

	function handleTagInput(e: Event) {
		const value = (e.target as HTMLInputElement).value;

		if (value.includes(',')) {
			addTag(value);
			input = '';
		}
		selectedSuggestionIndex = -1;
	}

	function handlePaste(e: ClipboardEvent) {
		const value = e.clipboardData?.getData('text') || '';
		if (value.includes(',')) {
			e.preventDefault();
			addTag(value);
			input = '';
		}
	}

	function isSemantic(tag: string) {
		return tag.includes(':');
	}

	function addTag(raw: string) {
		raw.split(',').forEach((p) => {
			let t = p.trim().toLowerCase();
			if (!t) return;
			t = t.replace(/^colour:/, 'color:');
			if (!tags.includes(t)) tags = [...tags, t];
		});
	}

	function handleKey(e: KeyboardEvent) {
		// Prevent tab from moving focus away from this input
		if (e.key === 'Tab') {
			e.preventDefault();
			// Allow arrow key navigation when tab is pressed
			if (e.shiftKey) {
				// Shift+Tab - go to previous suggestion (wrap to end)
				if (suggestions.length > 0) {
					if (selectedSuggestionIndex <= 0) {
						selectedSuggestionIndex = suggestions.length - 1;
					} else {
						selectedSuggestionIndex = selectedSuggestionIndex - 1;
					}
				}
			} else {
				// Tab - go to next suggestion (wrap to start)
				if (suggestions.length > 0) {
					if (selectedSuggestionIndex >= suggestions.length - 1) {
						selectedSuggestionIndex = 0;
					} else {
						selectedSuggestionIndex = selectedSuggestionIndex + 1;
					}
				}
			}
			return;
		}

		// Handle mobile keyboard action keys
		const isActionKey = ['Enter', 'Done', 'Go'].includes(e.key);

		if (isActionKey) {
			e.preventDefault();
		}

		if (e.key === 'ArrowDown') {
			e.preventDefault();
			if (suggestions.length > 0) {
				// Wrap to start if at end
				if (selectedSuggestionIndex >= suggestions.length - 1) {
					selectedSuggestionIndex = 0;
				} else {
					selectedSuggestionIndex = Math.min(selectedSuggestionIndex + 1, suggestions.length - 1);
				}
			}
			return;
		}

		if (e.key === 'ArrowUp') {
			e.preventDefault();
			if (suggestions.length > 0) {
				// Wrap to end if at start
				if (selectedSuggestionIndex <= 0) {
					selectedSuggestionIndex = suggestions.length - 1;
				} else {
					selectedSuggestionIndex = Math.max(selectedSuggestionIndex - 1, -1);
				}
			}
			return;
		}

		if (e.key === 'Enter' || e.key === 'Done' || e.key === 'Go') {
			// If there's a highlighted suggestion, select it
			if (selectedSuggestionIndex >= 0 && suggestions[selectedSuggestionIndex]) {
				selectSuggestion(suggestions[selectedSuggestionIndex]);
				return;
			}

			// If input has text, add it as tag
			if (input.trim()) {
				addTag(input);
				input = '';
				return;
			}

			// If input is empty but we have tags, show confirmation to upload
			if (tags.length > 0) {
				showConfirmUploadAlert = true;
				return;
			}

			return;
		}

		if (e.key === 'Backspace' && !input && tags.length) {
			tags = tags.slice(0, -1);
		}
	}

	function selectSuggestion(tag: string) {
		addTag(tag);
		input = '';
		selectedSuggestionIndex = -1;
	}

	function removeTag(tag: string) {
		tags = tags.filter((t) => t !== tag);
	}

	/** Core upload function – multi-format: generates AVIF+WebP+JPEG+JXL variants. */
	async function uploadOneOrMany() {
		// Add any pending input as tag before uploading
		if (input.trim()) {
			addTag(input);
			input = '';
		}

		if (!uploadFiles.length) return;
		uploading = true;
		uploadResults = [];
		uploadStatus = {};
		uploadProgress = 0;

		const startTs = Date.now(); // timestamp of button click

		for (let i = 0; i < uploadFiles.length; i++) {
			const file = uploadFiles[i];
			const fileKey = file.name;
			const fd = new FormData();

			try {
				/* -------- 1. Generate all full-size variants (AVIF, WebP, JPEG, JXL) ------- */
				uploadStatus[fileKey] = 'Generating full-size variants...';
				const fullVariants = await generateFullVariants(file);

				/* -------- 2. Generate all thumbnail variants (AVIF, WebP, JPEG) ------- */
				uploadStatus[fileKey] = 'Generating thumbnail variants...';
				const thumbVariants = await generateThumbnailVariants(file);

				/* -------- 3. Generate filenames -------------------------------- */
				const ts = startTs - i; // 1 ms decrement per file

				/* -------- 4. Attach all variants to FormData -------------------------------- */
				fd.append('file_full_avif', fullVariants.avif, `${ts}_full.avif`);
				fd.append('file_full_webp', fullVariants.webp, `${ts}_full.webp`);
				fd.append('file_full_jpg', fullVariants.jpg, `${ts}_full.jpg`);
				if (fullVariants.jxl) {
					fd.append('file_full_jxl', fullVariants.jxl, `${ts}_full.jxl`);
				}
				fd.append('file_thumb_avif', thumbVariants.avif, `${ts}_thumb.avif`);
				fd.append('file_thumb_webp', thumbVariants.webp, `${ts}_thumb.webp`);
				fd.append('file_thumb_jpg', thumbVariants.jpg, `${ts}_thumb.jpg`);

				if (tags.length > 0) {
					fd.append('tags', tags.join(','));
				}

				/* -------- 5. Call the API ------------------------------------- */
				uploadStatus[fileKey] = 'Uploading...';
				const response = await apiFetch('/api/portfolio/images', {
					method: 'POST',
					body: fd
				});

				if (!response.ok) {
					const errText = await response.text();
					uploadResults.push({
						name: file.name,
						error: errText || `Server error: ${response.status}`
					});
					uploadStatus[fileKey] = 'Failed';
				} else {
					const result = await response.json();
					uploadResults.push({
						name: file.name,
						url: result.url
					});
					uploadStatus[fileKey] = 'Complete';
				}
			} catch (err: unknown) {
				uploadResults.push({
					name: file.name,
					error: err instanceof Error ? err.message : 'Unknown error'
				});
				uploadStatus[fileKey] = 'Error';
			}

			uploadProgress = Math.round(((i + 1) / uploadFiles.length) * 100);
		}

		uploading = false;
		uploadFiles = [];
		filePreviews = [];
	}
</script>

<div class="space-y-6">
	<!-- Header -->
	<div class="mb-6">
		<h2 class="text-2xl font-bold">Upload Images</h2>
		<p class="mt-1 text-sm text-gray-600">
			Add portfolio images with tags and optional descriptions
		</p>
	</div>

	<!-- Main Card -->
	<Card.Root>
		<Card.Header>
			<div class="flex flex-col items-start justify-between gap-2 sm:flex-row sm:items-center">
				<div>
					<Card.Title>Drop or Select Files</Card.Title>
					<Card.Description
						>HEIC, AVIF, WebP, PNG, JPEG and more. Maximum 20MB per file</Card.Description
					>
				</div>
			</div>
		</Card.Header>

		<Card.Content class="space-y-6">
			<!-- File Drop Zone -->
			<FileDropZone onfiles={handleFilesDropped} accept="image/*" multiple>
				<div
					class="flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 px-4 py-8 text-center transition-colors hover:border-primary hover:bg-primary/5 sm:py-12"
				>
					<svg
						class="mb-3 h-8 w-8 text-gray-400"
						xmlns="http://www.w3.org/2000/svg"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
						<polyline points="17 8 12 3 7 8" />
						<line x1="12" y1="3" x2="12" y2="15" />
					</svg>
					<p class="text-sm font-medium text-gray-700">Drop images here or click to browse</p>
					<p class="mt-1 text-xs text-gray-500">
						HEIC, AVIF, WebP, PNG, JPEG, GIF &amp; more &mdash; max 20MB per file
					</p>
				</div>
			</FileDropZone>

			<!-- Selected Files Section -->
			{#if uploadFiles.length > 0}
				<div class="border-t pt-6">
					<div class="mb-4 flex items-center justify-between">
						<div>
							<h3 class="font-semibold text-gray-900">Ready to Upload</h3>
							<p class="text-sm text-gray-600">
								{uploadFiles.length}
								{uploadFiles.length === 1 ? 'file' : 'files'} selected
							</p>
						</div>
						<span
							class="inline-flex items-center rounded-full bg-blue-50 px-3 py-1 text-sm font-medium text-blue-700"
						>
							{uploadFiles.length} pending
						</span>
					</div>

					<!-- File List -->
					<div class="space-y-2">
						{#each uploadFiles as f (f.name)}
							{@const preview = filePreviews.find((p) => p.file === f)?.preview}
							{@const oversized = isFileTooBig(f)}
							<div
								class="flex items-center justify-between rounded-lg border bg-white p-3 sm:p-4"
								class:border-red-300={oversized}
								class:border-gray-200={!oversized}
							>
								<div class="flex min-w-0 flex-1 items-center gap-3">
									<div class="flex-shrink-0">
										{#if preview}
											<img src={preview} alt={f.name} class="h-12 w-12 rounded object-cover" />
										{:else}
											<div class="flex h-12 w-12 items-center justify-center rounded bg-blue-100">
												<svg
													class="h-6 w-6 text-blue-600"
													xmlns="http://www.w3.org/2000/svg"
													viewBox="0 0 24 24"
													fill="none"
													stroke="currentColor"
													stroke-width="2"
												>
													<rect x="3" y="3" width="18" height="18" rx="2" />
													<circle cx="8.5" cy="8.5" r="1.5" />
													<path d="M21 15l-5-5L5 21" />
												</svg>
											</div>
										{/if}
									</div>
									<div class="min-w-0 flex-1">
										<p
											class="truncate text-sm font-medium"
											class:text-gray-900={!oversized}
											class:text-red-900={oversized}
										>
											{f.name}
										</p>
										<div class="flex items-center gap-2">
											<p
												class="text-xs"
												class:text-gray-500={!oversized}
												class:text-red-600={oversized}
											>
												{formatFileSize(f.size)}
												{#if oversized}
													&mdash; exceeds 20MB limit
												{/if}
											</p>
											{#if uploading && uploadStatus[f.name]}
												<span
													class="inline-flex items-center gap-1.5 text-xs font-medium text-blue-600"
												>
													<svg
														class="h-3 w-3 animate-spin"
														xmlns="http://www.w3.org/2000/svg"
														viewBox="0 0 24 24"
														fill="none"
														stroke="currentColor"
														stroke-width="2"
													>
														<circle cx="12" cy="12" r="10" opacity="0.3" />
														<path d="M12 2A10 10 0 0 1 22 12" />
													</svg>
													{uploadStatus[f.name]}
												</span>
											{/if}
										</div>
									</div>
								</div>
								<button
									type="button"
									onclick={() => (uploadFiles = uploadFiles.filter((x) => x !== f))}
									class="ml-2 flex-shrink-0 rounded p-1 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-600 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 focus:outline-none"
									aria-label="Remove {f.name}"
								>
									<svg
										class="h-5 w-5"
										xmlns="http://www.w3.org/2000/svg"
										viewBox="0 0 24 24"
										fill="none"
										stroke="currentColor"
										stroke-width="2"
									>
										<path d="M18 6l-12 12" />
										<path d="M6 6l12 12" />
									</svg>
								</button>
							</div>
						{/each}
					</div>
				</div>
			{/if}

			<!-- Upload Results Section -->
			{#if uploadResults.length > 0}
				<div class="border-t pt-6">
					<h3 class="mb-4 font-semibold text-gray-900">Upload Results</h3>
					<div class="space-y-2">
						{#each uploadResults as result (result.url || result.name)}
							<div
								class="rounded-lg border p-3 sm:p-4"
								class:bg-red-50={result.error}
								class:border-red-200={result.error}
								class:bg-green-50={!result.error}
								class:border-green-200={!result.error}
							>
								<div class="flex items-start gap-3">
									<div class="flex-shrink-0 pt-0.5">
										{#if result.error}
											<svg
												class="h-5 w-5 text-red-600"
												xmlns="http://www.w3.org/2000/svg"
												viewBox="0 0 24 24"
												fill="none"
												stroke="currentColor"
												stroke-width="2"
											>
												<circle cx="12" cy="12" r="10" />
												<line x1="12" y1="8" x2="12" y2="12" />
												<line x1="12" y1="16" x2="12.01" y2="16" />
											</svg>
										{:else}
											<svg
												class="h-5 w-5 text-green-600"
												xmlns="http://www.w3.org/2000/svg"
												viewBox="0 0 24 24"
												fill="none"
												stroke="currentColor"
												stroke-width="2"
											>
												<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
												<polyline points="22 4 12 14.01 9 11.01" />
											</svg>
										{/if}
									</div>
									<div class="min-w-0 flex-1">
										<p
											class="truncate text-sm font-medium {result.error
												? 'text-red-900'
												: 'text-green-900'}"
										>
											{result.name}
										</p>
										{#if result.error}
											<p class="mt-1 text-xs text-red-700">{result.error}</p>
										{:else}
											<a
												href={result.url}
												target="_blank"
												rel="noopener noreferrer external"
												class="mt-1 inline-block text-xs text-green-700 underline hover:text-green-900"
											>
												View uploaded image →
											</a>
										{/if}
									</div>
								</div>
							</div>
						{/each}
					</div>
				</div>
			{/if}

			<!-- Tags Section -->
			<div class="border-t pt-6">
				<label for="tag-input" class="block text-sm font-semibold text-gray-900">Add Tags</label>
				<p class="mt-1 text-xs text-gray-600">
					Searchable tags help organize images. Use <code class="rounded bg-gray-100 px-1 py-0.5"
						>category:value</code
					> format.
				</p>

				<div class="relative">
					<div class="flex gap-2">
						<div
							class="flex min-h-[44px] w-full flex-wrap gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm transition-colors focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/10 focus-within:outline-none"
						>
							{#each tags as tag (tag)}
								<span
									class="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium transition-colors"
									class:bg-indigo-100={isSemantic(tag)}
									class:text-indigo-800={isSemantic(tag)}
									class:bg-emerald-100={!isSemantic(tag)}
									class:text-emerald-800={!isSemantic(tag)}
								>
									{tag}
									<button
										type="button"
										onclick={() => removeTag(tag)}
										class="inline-flex h-4 w-4 items-center justify-center rounded-full leading-none transition-colors hover:opacity-80 focus:ring-2 focus:ring-offset-2 focus:outline-none"
										class:text-indigo-700={isSemantic(tag)}
										class:focus:ring-indigo-500={isSemantic(tag)}
										class:text-emerald-700={!isSemantic(tag)}
										class:focus:ring-emerald-500={!isSemantic(tag)}
										aria-label="Remove {tag}"
									>
										×
									</button>
								</span>
							{/each}

							<input
								type="search"
								maxlength={256}
								enterkeyhint="done"
								autocomplete="off"
								autocorrect="off"
								autocapitalize="off"
								spellcheck="false"
								class="min-w-[100px] flex-1 border-none bg-transparent text-sm outline-none placeholder:text-gray-500"
								bind:this={inputRef}
								bind:value={input}
								onkeydown={handleKey}
								oninput={handleTagInput}
								onpaste={handlePaste}
								placeholder={tags.length ? '' : 'Add tags…'}
							/>
						</div>

						{#if input.length && suggestions.length}
							<div
								class="absolute top-full right-0 left-0 z-10 mt-1 rounded-md border bg-white shadow-md"
							>
								{#each suggestions as s, i (s)}
									{@const isHighlighted = isMobile ? i === 0 : i === selectedSuggestionIndex}
									<div
										class="cursor-pointer touch-manipulation px-3 py-3 text-sm {isHighlighted
											? 'bg-primary/10 font-medium text-primary'
											: 'hover:bg-gray-100'}"
										onclick={() => selectSuggestion(s)}
										onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && selectSuggestion(s)}
										role="button"
										tabindex="0"
									>
										{s}
									</div>
								{/each}
							</div>
						{/if}
					</div>
				</div>

				<!-- Action Buttons -->
				<div class="border-t pt-6">
					<div class="flex justify-end">
						<Button
							onclick={uploadOneOrMany}
							disabled={!uploadFiles.length || uploading || hasOversizedFiles}
						>
							{#if uploading}
								<svg
									class="mr-2 h-4 w-4 animate-spin"
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 24 24"
									fill="none"
									stroke="currentColor"
									stroke-width="2"
								>
									<circle cx="12" cy="12" r="10" opacity="0.3" />
									<path d="M12 2A10 10 0 0 1 22 12" />
								</svg>
								Uploading ({uploadProgress}%)
							{:else}
								<svg
									class="mr-2 h-4 w-4"
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 24 24"
									fill="none"
									stroke="currentColor"
									stroke-width="2"
								>
									<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
									<polyline points="17 8 12 3 7 8" />
									<line x1="12" y1="3" x2="12" y2="15" />
								</svg>
								Upload {uploadFiles.length > 1 ? uploadFiles.length + ' Files' : 'File'}
							{/if}
						</Button>
					</div>
				</div>
			</div></Card.Content
		>
	</Card.Root>

	<!-- Confirmation Dialog -->
	<AlertDialog.Root bind:open={showConfirmUploadAlert}>
		<AlertDialog.Content class="z-60">
			<AlertDialog.Header>
				<AlertDialog.Title>Confirm Upload</AlertDialog.Title>
				<AlertDialog.Description>
					You're about to upload {uploadFiles.length}
					{uploadFiles.length === 1 ? 'file' : 'files'} with {tags.length}
					{tags.length === 1 ? 'tag' : 'tags'}.
					{#if tags.length > 0}
						<div class="mt-3 flex flex-wrap gap-2">
							{#each tags as tag (tag)}
								<span
									class="inline-block rounded-full bg-gray-200 px-2.5 py-1 text-xs text-gray-800"
								>
									{tag}
								</span>
							{/each}
						</div>
					{/if}
				</AlertDialog.Description>
			</AlertDialog.Header>
			<AlertDialog.Footer>
				<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
				<AlertDialog.Action onclick={uploadOneOrMany}>Upload Now</AlertDialog.Action>
			</AlertDialog.Footer>
		</AlertDialog.Content>
	</AlertDialog.Root>
</div>
