<script lang="ts">
	import { onDestroy } from 'svelte';
	import type { FeedUpload } from '$lib/types';
	import { api } from '$lib/api';
	import { onSseEvent } from '$lib/sse';
	import { getUserId } from '$lib/auth';
	import { dataMode, pickMediaUrl } from '$lib/data-mode-store';
	import { doubletap } from '$lib/actions/doubletap';
	import { focusTrap } from '$lib/actions/focus-trap';
	import { scrollLock } from '$lib/actions/scroll-lock';
	import { modalInert } from '$lib/actions/modal-inert';
	import { toastError } from '$lib/toast-store';
	import { vibrate } from '$lib/haptics';
	import HeartBurst from './HeartBurst.svelte';

	const COMMENT_MAX = 500;

	interface CommentDto {
		id: string;
		upload_id: string;
		user_id: string;
		uploader_name: string;
		body: string;
		created_at: string;
	}

	interface Props {
		upload: FeedUpload;
		onclose: () => void;
		onlike: (id: string) => void;
	}

	let { upload, onclose, onlike }: Props = $props();

	let comments = $state<CommentDto[]>([]);
	let newComment = $state('');
	let loading = $state(false);
	let userId = getUserId();
	let heartBurst = $state(false);
	let burstTimer: ReturnType<typeof setTimeout> | null = null;

	const mediaSrc = $derived(pickMediaUrl($dataMode, upload));

	function triggerHeartBurst() {
		heartBurst = true;
		vibrate(10);
		onlike(upload.id);
		if (burstTimer) clearTimeout(burstTimer);
		burstTimer = setTimeout(() => (heartBurst = false), 700);
	}

	// Drop a comment live when it's deleted elsewhere (host moderation or the
	// author on another device), so the open panel doesn't show a ghost comment.
	const unsubCommentDeleted = onSseEvent('comment-deleted', (data) => {
		try {
			const { comment_id } = JSON.parse(data) as { comment_id: string };
			comments = comments.filter((c) => c.id !== comment_id);
		} catch { /* ignore */ }
	});

	onDestroy(() => {
		if (burstTimer) clearTimeout(burstTimer);
		unsubCommentDeleted();
	});

	// Only refetch when a *different* upload is shown. The feed reassigns the
	// `upload` prop object on every SSE like/comment count update; keying the
	// effect off the memoized id avoids a refetch storm that would also clobber
	// a just-posted optimistic comment.
	const uploadId = $derived(upload.id);

	$effect(() => {
		loadComments(uploadId);
	});

	async function loadComments(id: string) {
		try {
			comments = await api.get<CommentDto[]>(`/upload/${id}/comments`);
		} catch {
			// Background fetch — failure leaves the panel empty; reopening the lightbox retries.
		}
	}

	async function submitComment() {
		if (!newComment.trim()) return;
		loading = true;
		try {
			const comment = await api.post<CommentDto>(`/upload/${upload.id}/comments`, {
				body: newComment.trim()
			});
			comments = [...comments, comment];
			newComment = '';
		} catch (e) {
			toastError(e);
		} finally {
			loading = false;
		}
	}

	async function deleteComment(id: string) {
		try {
			await api.delete(`/comment/${id}`);
			comments = comments.filter((c) => c.id !== id);
		} catch (e) {
			toastError(e);
		}
	}

	function isVideo(mime: string): boolean {
		return mime.startsWith('video/');
	}

	function formatTime(iso: string): string {
		return new Date(iso).toLocaleString('de-DE', {
			day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit'
		});
	}
</script>

<div
	class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4"
	role="dialog"
	aria-modal="true"
	aria-labelledby="lightbox-title"
	use:focusTrap={{ onclose }}
	use:scrollLock
	use:modalInert
>
	<div class="flex max-h-[90vh] w-full max-w-2xl flex-col overflow-hidden rounded-xl bg-white dark:bg-gray-900">
		<!-- Media -->
		<div class="relative bg-black">
			<button
				onclick={onclose}
				aria-label="Schließen"
				class="absolute right-2 top-2 z-10 inline-flex min-h-11 min-w-11 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 active:bg-black/70"
			>
				<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
				</svg>
			</button>
			<div
				class="relative"
				use:doubletap
				ondoubletap={triggerHeartBurst}
			>
				{#if isVideo(upload.mime_type)}
					<video
						src={mediaSrc}
						controls
						class="max-h-[60vh] w-full object-contain"
						poster={upload.thumbnail_url ?? undefined}
					></video>
				{:else}
					<img
						src={mediaSrc}
						alt=""
						class="max-h-[60vh] w-full object-contain select-none"
						draggable="false"
					/>
				{/if}

				<HeartBurst active={heartBurst} />
			</div>
		</div>

		<!-- Info + Comments -->
		<div class="flex flex-1 flex-col overflow-hidden">
			<div class="border-b border-gray-100 p-3 dark:border-gray-800">
				<div class="flex items-center justify-between">
					<div>
						<span id="lightbox-title" class="font-medium text-gray-900 dark:text-gray-100">{upload.uploader_name}</span>
						<span class="ml-2 text-xs text-gray-400 dark:text-gray-500">{formatTime(upload.created_at)}</span>
					</div>
					<button
						onclick={() => onlike(upload.id)}
						class="flex items-center gap-1 rounded-full px-2.5 py-1 text-sm transition {
							upload.liked_by_me
								? 'bg-red-50 text-red-600 dark:bg-red-950/40 dark:text-red-300'
								: 'bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'
						}"
					>
						<svg class="h-4 w-4 {upload.liked_by_me ? 'fill-current' : ''}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
							<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
						</svg>
						{upload.like_count}
					</button>
				</div>
				{#if upload.caption}
					<p class="mt-1 text-sm text-gray-700 dark:text-gray-300">{upload.caption}</p>
				{/if}
			</div>

			<!-- Comments list -->
			<div class="flex-1 overflow-y-auto p-3">
				{#if comments.length === 0}
					<p class="text-center text-sm text-gray-400 dark:text-gray-500">Noch keine Kommentare.</p>
				{:else}
					<div class="space-y-3">
						{#each comments as comment (comment.id)}
							<div class="flex items-start gap-2">
								<div class="flex-1">
									<span class="text-sm font-medium text-gray-900 dark:text-gray-100">{comment.uploader_name}</span>
									<span class="ml-1 text-sm text-gray-700 dark:text-gray-300">{comment.body}</span>
									<div class="mt-0.5 text-xs text-gray-400 dark:text-gray-500">{formatTime(comment.created_at)}</div>
								</div>
								{#if comment.user_id === userId}
									<button
										onclick={() => deleteComment(comment.id)}
										class="shrink-0 text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400"
										aria-label="Löschen"
									>
										<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
											<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
										</svg>
									</button>
								{/if}
							</div>
						{/each}
					</div>
				{/if}
			</div>

			<!-- Comment input -->
			<form
				onsubmit={(e) => { e.preventDefault(); submitComment(); }}
				class="border-t border-gray-100 p-3 dark:border-gray-800"
			>
				<div class="flex gap-2">
					<input
						type="text"
						bind:value={newComment}
						placeholder="Kommentar schreiben..."
						maxlength={COMMENT_MAX}
						class="flex-1 rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:outline-none dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500"
					/>
					<button
						type="submit"
						disabled={loading || !newComment.trim()}
						class="rounded-lg bg-blue-600 px-3 py-2 text-sm font-medium text-white transition hover:bg-blue-700 active:bg-blue-700 disabled:opacity-50 dark:bg-blue-500 dark:hover:bg-blue-400 dark:active:bg-blue-400"
					>
						Senden
					</button>
				</div>
				<div
					class="mt-1 text-right text-xs"
					class:text-gray-400={newComment.length < 450}
					class:dark:text-gray-500={newComment.length < 450}
					class:text-amber-600={newComment.length >= 450 && newComment.length < COMMENT_MAX}
					class:dark:text-amber-400={newComment.length >= 450 && newComment.length < COMMENT_MAX}
					class:text-red-600={newComment.length >= COMMENT_MAX}
					class:dark:text-red-400={newComment.length >= COMMENT_MAX}
				>
					{newComment.length}/{COMMENT_MAX}
				</div>
			</form>
		</div>
	</div>
</div>
