import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Camera, ChevronLeft, ChevronRight, Download, Heart, Loader2, Share2, Sparkles, Trash2, X } from 'lucide-react';
import AppShell from '../components/AppShell';
import PhotoFrameTile from '../components/PhotoFrameTile';
import ShareSheet from '../components/ShareSheet';
import { useEventData } from '../context/EventDataContext';
import { createPhotoShareLink, deletePhoto, fetchGallery, fetchPhoto, likePhoto, unlikePhoto } from '../services/photosApi';
import { usePollGalleryDelta } from '../hooks/usePollGalleryDelta';
import { useGuestThemeVariant } from '../lib/guestTheme';
import { useTranslation } from '@/shared/guest/i18n/useTranslation';
import { useLocale } from '@/shared/guest/i18n/LocaleContext';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { buildEventPath } from '../lib/routes';
import { getBentoSurfaceTokens } from '../lib/bento';
import { usePollStats } from '../hooks/usePollStats';
import { pushGuestToast } from '../lib/toast';

type GalleryFilter = 'latest' | 'popular' | 'mine' | 'photobooth';

type GalleryTile = {
  id: number;
  imageUrl: string;
  likes: number;
  createdAt?: string | null;
  ingestSource?: string | null;
  sessionId?: string | null;
  isMine?: boolean;
  taskId?: number | null;
  taskLabel?: string | null;
  emotion?: {
    name?: string | null;
    icon?: string | null;
    color?: string | null;
  } | null;
};

type LightboxPhoto = {
  id: number;
  imageUrl: string;
  likes: number;
  isMine?: boolean;
  taskId?: number | null;
  taskLabel?: string | null;
  emotion?: {
    name?: string | null;
    icon?: string | null;
    color?: string | null;
  } | null;
};

function normalizeImageUrl(src?: string | null) {
  if (!src) {
    return '';
  }

  if (/^https?:/i.test(src)) {
    return src;
  }

  let cleanPath = src.replace(/^\/+/g, '').replace(/\/+/g, '/');
  if (!cleanPath.startsWith('storage/')) {
    cleanPath = `storage/${cleanPath}`;
  }

  return `/${cleanPath}`.replace(/\/+/g, '/');
}

function readMyPhotoIds(): Set<number> {
  try {
    const raw = localStorage.getItem('my-photo-ids');
    const parsed = raw ? JSON.parse(raw) : [];
    if (!Array.isArray(parsed)) {
      return new Set();
    }
    const ids = parsed.map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0);
    return new Set(ids);
  } catch {
    return new Set();
  }
}

export default function GalleryScreen() {
  const { token, event } = useEventData();
  const { t } = useTranslation();
  const { locale } = useLocale();
  const navigate = useNavigate();
  const [searchParams, setSearchParams] = useSearchParams();
  const { isDark } = useGuestThemeVariant();
  const bentoSurface = getBentoSurfaceTokens(isDark);
  const cardShadow = bentoSurface.shadow;
  const hardShadow = isDark
    ? '0 18px 0 rgba(2, 6, 23, 0.55), 0 32px 40px rgba(2, 6, 23, 0.55)'
    : '0 18px 0 rgba(15, 23, 42, 0.22), 0 30px 36px rgba(15, 23, 42, 0.2)';
  const mutedButton = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.06)';
  const mutedButtonBorder = isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(15, 23, 42, 0.12)';
  const [photos, setPhotos] = React.useState<GalleryTile[]>([]);
  const [loading, setLoading] = React.useState(false);
  const { data: delta } = usePollGalleryDelta(token ?? null, { locale });
  const { stats } = usePollStats(token ?? null, 12000);
  const [filter, setFilter] = React.useState<GalleryFilter>('latest');
  const uploadPath = React.useMemo(() => buildEventPath(token ?? null, '/upload'), [token]);
  const numberFormatter = React.useMemo(() => new Intl.NumberFormat(locale), [locale]);
  const [lightboxPhoto, setLightboxPhoto] = React.useState<LightboxPhoto | null>(null);
  const [lightboxLoading, setLightboxLoading] = React.useState(false);
  const [lightboxError, setLightboxError] = React.useState<'notFound' | 'loadFailed' | null>(null);
  const [likesById, setLikesById] = React.useState<Record<number, number>>({});
  const [shareSheet, setShareSheet] = React.useState<{ url: string | null; loading: boolean }>({
    url: null,
    loading: false,
  });
  const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
  const [deleteBusy, setDeleteBusy] = React.useState(false);
  const [deleteConfirmMounted, setDeleteConfirmMounted] = React.useState(false);
  const [deleteConfirmVisible, setDeleteConfirmVisible] = React.useState(false);
  const [likedIds, setLikedIds] = React.useState<Set<number>>(new Set());
  const touchStartX = React.useRef<number | null>(null);
  const fallbackAttemptedRef = React.useRef(false);
  const pendingNotFoundRef = React.useRef(false);
  const photosRef = React.useRef<GalleryTile[]>([]);
  const galleryLoadingRef = React.useRef(Boolean(token));
  const transitionDirectionRef = React.useRef<'next' | 'prev'>('next');
  const lastLightboxPhotoRef = React.useRef<LightboxPhoto | null>(null);
  const [lightboxTransition, setLightboxTransition] = React.useState<{
    from: LightboxPhoto | null;
    to: LightboxPhoto | null;
    direction: 'next' | 'prev';
    active: boolean;
  }>({ from: null, to: null, direction: 'next', active: false });
  const [lightboxMounted, setLightboxMounted] = React.useState(false);
  const [lightboxVisible, setLightboxVisible] = React.useState(false);

  React.useEffect(() => {
    if (deleteConfirmOpen) {
      setDeleteConfirmVisible(false);
      setDeleteConfirmMounted(true);
      let frame1 = 0;
      let frame2 = 0;
      frame1 = window.requestAnimationFrame(() => {
        frame2 = window.requestAnimationFrame(() => setDeleteConfirmVisible(true));
      });
      return () => {
        window.cancelAnimationFrame(frame1);
        window.cancelAnimationFrame(frame2);
      };
    }

    setDeleteConfirmVisible(false);
    const timer = window.setTimeout(() => setDeleteConfirmMounted(false), 220);
    return () => window.clearTimeout(timer);
  }, [deleteConfirmOpen]);

  React.useEffect(() => {
    if (!token) {
      setPhotos([]);
      galleryLoadingRef.current = false;
      return;
    }

    let active = true;
    setLoading(true);
    galleryLoadingRef.current = true;

    fetchGallery(token, { limit: 18, locale })
      .then((response) => {
        if (!active) return;
        const list = Array.isArray(response.data) ? response.data : [];
        const mapped = list
          .map((photo) => {
            const record = photo as Record<string, unknown>;
            const id = Number(record.id ?? 0);
            const likesCount = typeof record.likes_count === 'number' ? record.likes_count : 0;
            const imageUrl = normalizeImageUrl(
              (record.thumbnail_url as string | null | undefined)
                ?? (record.thumbnail_path as string | null | undefined)
                ?? (record.file_path as string | null | undefined)
                ?? (record.full_url as string | null | undefined)
                ?? (record.url as string | null | undefined)
                ?? (record.image_url as string | null | undefined)
            );
            const rawTaskId = Number(record.task_id ?? record.taskId ?? 0);
            const taskId = Number.isFinite(rawTaskId) && rawTaskId > 0 ? rawTaskId : null;
            const taskLabel =
              typeof record.task_title === 'string'
                ? record.task_title
                : typeof record.task_name === 'string'
                  ? record.task_name
                  : typeof record.task === 'string'
                    ? record.task
                    : typeof record.task_label === 'string'
                      ? record.task_label
                      : null;
            const rawEmotion = (record.emotion as Record<string, unknown> | null) ?? null;
            const rawIsMine = record.is_mine ?? record.isMine;
            const isMine = rawIsMine === true || rawIsMine === 1 || rawIsMine === '1';
            const emotionName =
              typeof rawEmotion?.name === 'string'
                ? rawEmotion.name
                : typeof record.emotion_name === 'string'
                  ? record.emotion_name
                  : null;
            const emotionIcon =
              typeof rawEmotion?.icon === 'string'
                ? rawEmotion.icon
                : typeof rawEmotion?.emoji === 'string'
                  ? rawEmotion.emoji
                  : typeof record.emotion_icon === 'string'
                    ? record.emotion_icon
                    : typeof record.emotion_emoji === 'string'
                      ? record.emotion_emoji
                      : null;
            const emotionColor =
              typeof rawEmotion?.color === 'string'
                ? rawEmotion.color
                : typeof record.emotion_color === 'string'
                  ? record.emotion_color
                  : null;
            const emotion = emotionName || emotionIcon || emotionColor
              ? { name: emotionName, icon: emotionIcon, color: emotionColor }
              : null;
            return {
              id,
              imageUrl,
              likes: likesCount,
              createdAt: typeof record.created_at === 'string' ? record.created_at : null,
              ingestSource: typeof record.ingest_source === 'string' ? record.ingest_source : null,
              sessionId: typeof record.session_id === 'string' ? record.session_id : null,
              isMine,
              taskId,
              taskLabel,
              emotion,
            };
          })
          .filter((item) => item.id && item.imageUrl);
        setPhotos(mapped);
      })
      .catch((error) => {
        console.error('Failed to load gallery', error);
        if (active) {
          setPhotos([]);
        }
      })
      .finally(() => {
        if (active) {
          setLoading(false);
        }
        galleryLoadingRef.current = false;
      });

    return () => {
      active = false;
    };
  }, [token, locale]);

  React.useEffect(() => {
    photosRef.current = photos;
  }, [photos]);

  const [myPhotoIds, setMyPhotoIds] = React.useState<Set<number>>(() => readMyPhotoIds());

  React.useEffect(() => {
    setMyPhotoIds(readMyPhotoIds());
  }, [token]);

  const filteredPhotos = React.useMemo(() => {
    let list = photos.slice();
    if (filter === 'popular') {
      list.sort((a, b) => (b.likes ?? 0) - (a.likes ?? 0));
    } else if (filter === 'mine') {
      list = list.filter((photo) => Boolean(photo.isMine) || myPhotoIds.has(photo.id));
    } else if (filter === 'photobooth') {
      list = list.filter((photo) => photo.ingestSource === 'photobooth');
      list.sort((a, b) => new Date(b.createdAt ?? 0).getTime() - new Date(a.createdAt ?? 0).getTime());
    } else {
      list.sort((a, b) => new Date(b.createdAt ?? 0).getTime() - new Date(a.createdAt ?? 0).getTime());
    }
    return list;
  }, [filter, myPhotoIds, photos]);

  const displayPhotos = filteredPhotos;
  const leftColumn = displayPhotos.filter((_, index) => index % 2 === 0);
  const rightColumn = displayPhotos.filter((_, index) => index % 2 === 1);
  const isEmpty = !loading && displayPhotos.length === 0;
  const isSingle = !loading && displayPhotos.length === 1;
  const selectedPhotoId = Number(searchParams.get('photo') ?? 0);
  const lightboxIndex = React.useMemo(() => {
    if (!selectedPhotoId) {
      return -1;
    }
    return displayPhotos.findIndex((item) => item.id === selectedPhotoId);
  }, [displayPhotos, selectedPhotoId]);
  const lightboxSelected = lightboxIndex >= 0 ? displayPhotos[lightboxIndex] : null;
  const lightboxOpen = Boolean(selectedPhotoId);
  const canDelete = Boolean(lightboxPhoto && (lightboxPhoto.isMine || myPhotoIds.has(lightboxPhoto.id)));

  React.useEffect(() => {
    if (filter === 'photobooth' && !photos.some((photo) => photo.ingestSource === 'photobooth')) {
      setFilter('latest');
    }
  }, [filter, photos]);

  React.useEffect(() => {
    fallbackAttemptedRef.current = false;
  }, [selectedPhotoId]);

  React.useEffect(() => {
    if (!lightboxOpen || !selectedPhotoId) {
      return;
    }
    if (lightboxIndex >= 0) {
      return;
    }
    if (filter !== 'latest' && !fallbackAttemptedRef.current) {
      fallbackAttemptedRef.current = true;
      setFilter('latest');
    }
  }, [filter, lightboxIndex, lightboxOpen, selectedPhotoId]);
  const newUploads = React.useMemo(() => {
    if (delta.photos.length === 0) {
      return 0;
    }
    const existing = new Set(photos.map((item) => item.id));
    return delta.photos.reduce((count, photo) => {
      const id = Number((photo as Record<string, unknown>).id ?? 0);
      if (id && !existing.has(id)) {
        return count + 1;
      }
      return count;
    }, 0);
  }, [delta.photos, photos]);
  const openLightbox = React.useCallback(
    (photoId: number) => {
      if (!token) return;
      const next = new URLSearchParams(searchParams);
      next.set('photo', String(photoId));
      setSearchParams(next, { replace: false });
    },
    [searchParams, setSearchParams, token]
  );
  const closeLightbox = React.useCallback(() => {
    const next = new URLSearchParams(searchParams);
    next.delete('photo');
    setSearchParams(next, { replace: true });
  }, [searchParams, setSearchParams]);

  const removeMyPhotoId = React.useCallback((photoId: number) => {
    setMyPhotoIds((prev) => {
      const next = new Set(prev);
      next.delete(photoId);
      return next;
    });
    try {
      const raw = localStorage.getItem('my-photo-ids');
      const parsed = raw ? JSON.parse(raw) : [];
      if (Array.isArray(parsed)) {
        const next = parsed.filter((value) => Number(value) !== photoId);
        localStorage.setItem('my-photo-ids', JSON.stringify(next));
      }
    } catch (error) {
      console.warn('Failed to update my-photo-ids', error);
    }
  }, []);

  React.useEffect(() => {
    if (delta.photos.length === 0) {
      return;
    }
    setPhotos((prev) => {
      const existing = new Set(prev.map((item) => item.id));
      const mapped = delta.photos
        .map((photo) => {
          const record = photo as Record<string, unknown>;
          const id = Number(record.id ?? 0);
          const likesCount = typeof record.likes_count === 'number' ? record.likes_count : 0;
          const imageUrl = normalizeImageUrl(
            (record.thumbnail_url as string | null | undefined)
              ?? (record.thumbnail_path as string | null | undefined)
              ?? (record.file_path as string | null | undefined)
              ?? (record.full_url as string | null | undefined)
              ?? (record.url as string | null | undefined)
              ?? (record.image_url as string | null | undefined)
          );
          if (!id || !imageUrl || existing.has(id)) {
            return null;
          }
          const rawIsMine = record.is_mine ?? record.isMine;
          const isMine = rawIsMine === true || rawIsMine === 1 || rawIsMine === '1';
          return {
            id,
            imageUrl,
            likes: likesCount,
            createdAt: typeof record.created_at === 'string' ? record.created_at : null,
            ingestSource: typeof record.ingest_source === 'string' ? record.ingest_source : null,
            sessionId: typeof record.session_id === 'string' ? record.session_id : null,
            isMine,
          } satisfies GalleryTile;
        })
        .filter(Boolean) as GalleryTile[];
      if (mapped.length === 0) {
        return prev;
      }
      return [...mapped, ...prev];
    });
  }, [delta.photos]);

  const heroStatsLine = t(
    'galleryPage.hero.stats',
    {
      photoCount: numberFormatter.format(photos.length),
      likeCount: numberFormatter.format(stats.likesCount ?? 0),
      guestCount: numberFormatter.format(stats.onlineGuests || stats.guestCount || 0),
    },
    `${numberFormatter.format(photos.length)} Fotos · ${numberFormatter.format(stats.likesCount ?? 0)} ❤️ · ${numberFormatter.format(stats.onlineGuests || stats.guestCount || 0)} Gäste online`
  );

  React.useEffect(() => {
    setLikesById((prev) => {
      const next = { ...prev };
      for (const photo of photos) {
        if (next[photo.id] === undefined) {
          next[photo.id] = photo.likes;
        }
      }
      return next;
    });
  }, [photos]);

  React.useEffect(() => {
    if (!lightboxOpen) {
      setLightboxPhoto(null);
      setLightboxLoading(false);
      setLightboxError(null);
      pendingNotFoundRef.current = false;
      setDeleteConfirmOpen(false);
      setDeleteBusy(false);
      return;
    }

    const seed = lightboxSelected
      ? {
          id: lightboxSelected.id,
          imageUrl: lightboxSelected.imageUrl,
          likes: lightboxSelected.likes,
          isMine: lightboxSelected.isMine,
          taskId: lightboxSelected.taskId ?? null,
          taskLabel: lightboxSelected.taskLabel ?? null,
          emotion: lightboxSelected.emotion ?? null,
        }
      : null;
    if (seed) {
      setLightboxPhoto(seed);
    }
    const hasSeed = Boolean(seed);

    if (hasSeed) {
      setLightboxLoading(false);
      return;
    }

    let active = true;
    const maxRetryMs = 10_000;
    const retryDelayMs = 1500;
    let timedOut = false;
    const timeoutId = window.setTimeout(() => {
      timedOut = true;
    }, maxRetryMs);
    setLightboxLoading(true);
    setLightboxError(null);
    const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

    const loadPhoto = async () => {
      let lastError: unknown = null;

      while (active && !timedOut) {
        try {
          const photo = await fetchPhoto(selectedPhotoId, locale);
          if (!active) return;
          if (photo) {
        const mapped = mapFullPhoto(photo as Record<string, unknown>);
            if (mapped) {
              setLightboxPhoto(mapped);
              setLikesById((prev) => ({ ...prev, [mapped.id]: mapped.likes }));
              setLightboxLoading(false);
              return;
            }
          }
          lastError = { status: 404 };
        } catch (error) {
          console.error('Lightbox photo load failed', error);
          lastError = error;
        }

        if (!active || timedOut) break;
        await sleep(retryDelayMs);
      }

      if (!active) return;
      const status = (lastError as { status?: number } | null)?.status;
      setLightboxError(status === 404 ? 'notFound' : 'loadFailed');
      setLightboxLoading(false);
    };

    void loadPhoto();

    return () => {
      active = false;
      window.clearTimeout(timeoutId);
    };
  }, [lightboxOpen, lightboxSelected, locale, selectedPhotoId]);

  React.useLayoutEffect(() => {
    if (!lightboxPhoto) {
      lastLightboxPhotoRef.current = null;
      setLightboxTransition({ from: null, to: null, direction: 'next', active: false });
      return;
    }

    const previous = lastLightboxPhotoRef.current;
    if (!previous || previous.id === lightboxPhoto.id) {
      lastLightboxPhotoRef.current = lightboxPhoto;
      setLightboxTransition({ from: null, to: lightboxPhoto, direction: transitionDirectionRef.current, active: false });
      return;
    }

    const direction = transitionDirectionRef.current;
    setLightboxTransition({ from: previous, to: lightboxPhoto, direction, active: false });
    const raf = window.requestAnimationFrame(() => {
      setLightboxTransition((state) => ({ ...state, active: true }));
    });
    const timeout = window.setTimeout(() => {
      setLightboxTransition({ from: null, to: lightboxPhoto, direction, active: false });
    }, 420);
    lastLightboxPhotoRef.current = lightboxPhoto;

    return () => {
      window.cancelAnimationFrame(raf);
      window.clearTimeout(timeout);
    };
  }, [lightboxPhoto]);

  React.useEffect(() => {
    if (!lightboxOpen) {
      document.body.style.overflow = '';
      return;
    }
    document.body.style.overflow = 'hidden';
    const handleKey = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        closeLightbox();
      }
    };
    window.addEventListener('keydown', handleKey);
    return () => {
      document.body.style.overflow = '';
      window.removeEventListener('keydown', handleKey);
    };
  }, [lightboxOpen]);

  React.useEffect(() => {
    if (lightboxOpen) {
      setLightboxMounted(true);
      const raf = window.requestAnimationFrame(() => {
        setLightboxVisible(true);
      });
      return () => window.cancelAnimationFrame(raf);
    }

    setLightboxVisible(false);
    const timeout = window.setTimeout(() => {
      setLightboxMounted(false);
    }, 240);
    return () => window.clearTimeout(timeout);
  }, [lightboxOpen]);

  React.useEffect(() => {
    if (!pendingNotFoundRef.current) return;
    if (loading || galleryLoadingRef.current) return;
    if (lightboxSelected || photosRef.current.some((photo) => photo.id === selectedPhotoId)) {
      pendingNotFoundRef.current = false;
      return;
    }
    pendingNotFoundRef.current = false;
    setLightboxError('notFound');
  }, [lightboxSelected, loading]);

  React.useEffect(() => {
    if (!lightboxOpen || !lightboxError) {
      return;
    }
    pushGuestToast({
      text: lightboxError === 'notFound'
        ? t('lightbox.errors.notFound', 'Photo not found')
        : t('lightbox.errors.loadFailed', 'Failed to load photo'),
      type: 'warning',
    });
    closeLightbox();
  }, [closeLightbox, lightboxError, lightboxOpen, t]);

  const goPrev = React.useCallback(() => {
    if (lightboxIndex <= 0) return;
    const prevId = displayPhotos[lightboxIndex - 1]?.id;
    if (prevId) {
      transitionDirectionRef.current = 'prev';
      openLightbox(prevId);
    }
  }, [displayPhotos, lightboxIndex, openLightbox]);

  const goNext = React.useCallback(() => {
    if (lightboxIndex < 0 || lightboxIndex >= displayPhotos.length - 1) return;
    const nextId = displayPhotos[lightboxIndex + 1]?.id;
    if (nextId) {
      transitionDirectionRef.current = 'next';
      openLightbox(nextId);
    }
  }, [displayPhotos, lightboxIndex, openLightbox]);

  const handleLike = React.useCallback(async () => {
    if (!lightboxPhoto) return;
    const isLiked = likedIds.has(lightboxPhoto.id);
    const current = likesById[lightboxPhoto.id] ?? lightboxPhoto.likes;
    const nextCount = Math.max(0, current + (isLiked ? -1 : 1));
    setLikedIds((prev) => {
      const next = new Set(prev);
      if (isLiked) {
        next.delete(lightboxPhoto.id);
      } else {
        next.add(lightboxPhoto.id);
      }
      return next;
    });
    setLikesById((prev) => ({ ...prev, [lightboxPhoto.id]: nextCount }));
    try {
      const count = isLiked ? await unlikePhoto(lightboxPhoto.id) : await likePhoto(lightboxPhoto.id);
      setLikesById((prev) => ({ ...prev, [lightboxPhoto.id]: count }));
    } catch (error) {
      console.error('Like failed', error);
      setLikedIds((prev) => {
        const next = new Set(prev);
        if (isLiked) {
          next.add(lightboxPhoto.id);
        } else {
          next.delete(lightboxPhoto.id);
        }
        return next;
      });
      setLikesById((prev) => ({ ...prev, [lightboxPhoto.id]: current }));
    }
  }, [lightboxPhoto, likedIds, likesById]);

  const handleDelete = React.useCallback(async () => {
    if (!lightboxPhoto || !token || deleteBusy) return;
    setDeleteBusy(true);
    try {
      await deletePhoto(token, lightboxPhoto.id);
      setPhotos((prev) => prev.filter((photo) => photo.id !== lightboxPhoto.id));
      setLikesById((prev) => {
        const next = { ...prev };
        delete next[lightboxPhoto.id];
        return next;
      });
      setLikedIds((prev) => {
        const next = new Set(prev);
        next.delete(lightboxPhoto.id);
        return next;
      });
      removeMyPhotoId(lightboxPhoto.id);
      setDeleteConfirmOpen(false);
      setDeleteBusy(false);
      closeLightbox();
      pushGuestToast({
        text: t('galleryPage.lightbox.deletedToast', 'Photo deleted.'),
        type: 'success',
      });
    } catch (error) {
      console.error('Failed to delete photo', error);
      setDeleteBusy(false);
      pushGuestToast({
        text: t('galleryPage.lightbox.deleteFailed', 'Photo could not be deleted.'),
        type: 'error',
      });
    }
  }, [closeLightbox, deleteBusy, lightboxPhoto, removeMyPhotoId, t, token]);

  const shareTitle = event?.name ?? t('share.title', 'Shared photo');
  const shareText = t('share.shareText', 'Check out this moment on Fotospiel.');

  const openShareSheet = React.useCallback(async () => {
    if (!lightboxPhoto || !token) return;
    setShareSheet({ url: null, loading: true });
    try {
      const payload = await createPhotoShareLink(token, lightboxPhoto.id);
      const url = payload?.url ?? null;
      setShareSheet({ url, loading: false });
    } catch (error) {
      console.error('Share failed', error);
      pushGuestToast({ text: t('share.error', 'Share failed'), type: 'error' });
      setShareSheet({ url: null, loading: false });
    }
  }, [lightboxPhoto, t, token]);

  const closeShareSheet = React.useCallback(() => {
    setShareSheet({ url: null, loading: false });
  }, []);

  const shareWhatsApp = React.useCallback(
    (url?: string | null) => {
      if (!url) return;
      const waUrl = `https://wa.me/?text=${encodeURIComponent(`${shareText} ${url}`)}`;
      window.open(waUrl, '_blank', 'noopener');
      closeShareSheet();
    },
    [closeShareSheet, shareText]
  );

  const shareMessages = React.useCallback(
    (url?: string | null) => {
      if (!url) return;
      const smsUrl = `sms:?&body=${encodeURIComponent(`${shareText} ${url}`)}`;
      window.open(smsUrl, '_blank', 'noopener');
      closeShareSheet();
    },
    [closeShareSheet, shareText]
  );

  const copyLink = React.useCallback(
    async (url?: string | null) => {
      if (!url) return;
      try {
        await navigator.clipboard?.writeText(url);
        pushGuestToast({ text: t('share.copySuccess', 'Link copied!') });
      } catch (error) {
        console.error('Copy failed', error);
        pushGuestToast({ text: t('share.copyError', 'Link could not be copied.'), type: 'error' });
      } finally {
        closeShareSheet();
      }
    },
    [closeShareSheet, t]
  );

  const shareNative = React.useCallback(
    (url?: string | null) => {
      if (!url) return;
      const data: ShareData = {
        title: shareTitle,
        text: shareText,
        url,
      };
      if (navigator.share && (!navigator.canShare || navigator.canShare(data))) {
        navigator.share(data).catch(() => undefined);
        closeShareSheet();
        return;
      }
      void copyLink(url);
    },
    [closeShareSheet, copyLink, shareText, shareTitle]
  );

  const downloadPhoto = React.useCallback((photo?: LightboxPhoto | null) => {
    if (!photo?.imageUrl) return;
    const link = document.createElement('a');
    link.href = photo.imageUrl;
    link.download = `photo-${photo.id}.jpg`;
    link.rel = 'noreferrer';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }, []);

  const handleTouchStart = (event: React.TouchEvent) => {
    touchStartX.current = event.touches[0]?.clientX ?? null;
  };

  const handleTouchEnd = (event: React.TouchEvent) => {
    if (touchStartX.current === null) {
      return;
    }
    const endX = event.changedTouches[0]?.clientX ?? null;
    if (endX === null) {
      touchStartX.current = null;
      return;
    }
    const delta = endX - touchStartX.current;
    touchStartX.current = null;
    if (Math.abs(delta) < 60) {
      return;
    }
    if (delta > 0) {
      transitionDirectionRef.current = 'prev';
      goPrev();
      return;
    }
    transitionDirectionRef.current = 'next';
    goNext();
  };

  return (
    <AppShell>
      <YStack gap="$4">
        <YStack
          padding="$2"
          borderRadius="$bentoLg"
          backgroundColor={bentoSurface.backgroundColor}
          borderWidth={1}
          borderBottomWidth={3}
          borderColor={bentoSurface.borderColor}
          borderBottomColor={bentoSurface.borderBottomColor}
          gap="$2"
          position="relative"
          overflow="hidden"
          style={{
            backgroundImage: isDark
              ? 'radial-gradient(120% 120% at 20% 20%, rgba(56, 189, 248, 0.18), transparent 55%), radial-gradient(120% 120% at 80% 15%, rgba(251, 113, 133, 0.18), transparent 60%)'
              : 'radial-gradient(130% 130% at 20% 20%, color-mix(in oklab, var(--guest-primary, #0EA5E9) 25%, white), transparent 55%), radial-gradient(120% 120% at 80% 0%, color-mix(in oklab, var(--guest-secondary, #F43F5E) 18%, white), transparent 60%)',
            boxShadow: hardShadow,
          }}
        >
          <XStack alignItems="center" gap="$2">
            <Sparkles size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
            <Text fontSize="$1" color="$color" opacity={0.7} textTransform="uppercase" letterSpacing={1.2}>
              {t('galleryPage.hero.label', 'Live-Galerie')}
            </Text>
          </XStack>
          <XStack alignItems="center" justifyContent="space-between" flexWrap="wrap" gap="$2">
            <Text fontSize="$6" fontFamily="$display" fontWeight="$8">
              {event?.name ?? t('galleryPage.hero.eventFallback', 'Euer Event')}
            </Text>
            <Button
              size="$3"
              backgroundColor="$primary"
              borderRadius="$pill"
              onPress={() => navigate(uploadPath)}
              pressStyle={{ y: 2 }}
              style={{
                boxShadow: isDark ? '0 8px 0 rgba(2, 6, 23, 0.55)' : '0 8px 0 rgba(15, 23, 42, 0.18)',
              }}
            >
              {t('galleryPage.hero.upload', 'Neues Foto hochladen')}
            </Button>
          </XStack>
          {newUploads > 0 ? (
            <YStack
              paddingHorizontal="$2.5"
              paddingVertical="$1"
              borderRadius="$pill"
              backgroundColor={mutedButton}
              borderWidth={1}
              borderColor={mutedButtonBorder}
              alignSelf="flex-start"
            >
              <Text fontSize="$1" fontWeight="$6">
                {t('galleryPage.feed.newUploads', { count: newUploads }, '{count} neue Uploads sind da.')}
              </Text>
            </YStack>
          ) : null}
          <Text fontSize="$1" color="$color" opacity={0.7}>
            {heroStatsLine}
          </Text>
          <YStack
            gap="$1"
            paddingTop="$1"
            borderTopWidth={1}
            borderTopColor={bentoSurface.borderColor}
          >
            <XStack gap="$1.5" flexWrap="wrap" justifyContent="center">
              {(
                [
                  { value: 'latest', label: t('galleryPage.filters.latest', 'Newest') },
                  { value: 'popular', label: t('galleryPage.filters.popular', 'Popular') },
                  { value: 'mine', label: t('galleryPage.filters.mine', 'My photos') },
                  photos.some((photo) => photo.ingestSource === 'photobooth')
                    ? { value: 'photobooth', label: t('galleryPage.filters.photobooth', 'Photo booth') }
                    : null,
                ].filter(Boolean) as Array<{ value: GalleryFilter; label: string }>
              ).map((chip) => (
                <Button
                  key={chip.value}
                  size="$2"
                  backgroundColor={filter === chip.value ? '$primary' : mutedButton}
                  borderRadius="$pill"
                  borderWidth={1}
                  borderColor={filter === chip.value ? '$primary' : mutedButtonBorder}
                  onPress={() => setFilter(chip.value)}
                >
                  <Text fontSize="$1" fontWeight="$6" color={filter === chip.value ? '#FFFFFF' : undefined}>
                    {chip.label}
                  </Text>
                </Button>
              ))}
            </XStack>
          </YStack>
        </YStack>

        {isEmpty ? (
          <YStack
            padding="$4"
            borderRadius="$bento"
            backgroundColor={bentoSurface.backgroundColor}
            borderWidth={1}
            borderBottomWidth={3}
            borderColor={bentoSurface.borderColor}
            borderBottomColor={bentoSurface.borderBottomColor}
            gap="$3"
            alignItems="center"
            style={{
              boxShadow: cardShadow,
            }}
          >
            <YStack
              width={64}
              height={64}
              borderRadius={20}
              backgroundColor="$primary"
              alignItems="center"
              justifyContent="center"
              style={{ boxShadow: cardShadow }}
            >
              <Camera size={28} color="#FFFFFF" />
            </YStack>
            <Text fontSize="$4" fontWeight="$7" textAlign="center">
              {t('galleryPage.emptyTitle', 'Noch keine Fotos')}
            </Text>
            <Text fontSize="$2" color="$color" opacity={0.7} textAlign="center">
              {t('galleryPage.emptyDescription', 'Lade das erste Foto hoch und starte die Galerie.')}
            </Text>
            <Button
              size="$3"
              backgroundColor="$primary"
              borderRadius="$pill"
              onPress={() => navigate(uploadPath)}
            >
              <Text fontSize="$2" fontWeight="$7" color="#FFFFFF">
                {t('galleryPage.emptyCta', 'Foto hochladen')}
              </Text>
            </Button>
          </YStack>
        ) : isSingle ? (
          <YStack gap="$3">
            <Button unstyled onPress={() => openLightbox(displayPhotos[0].id)}>
              <PhotoFrameTile height={360} borderRadius="$bento">
                <YStack flex={1} width="100%" height="100%" alignItems="center" justifyContent="center">
                  <img
                    src={displayPhotos[0].imageUrl}
                    alt={t('galleryPage.photo.alt', { id: displayPhotos[0].id, suffix: '' }, `Foto ${displayPhotos[0].id}`)}
                    loading="eager"
                    decoding="async"
                    style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                  />
                </YStack>
              </PhotoFrameTile>
            </Button>
            <Button unstyled onPress={() => navigate(uploadPath)}>
              <PhotoFrameTile height={160} borderRadius="$bento">
                <YStack flex={1} alignItems="center" justifyContent="center" gap="$2" padding="$3">
                  <YStack
                    width={48}
                    height={48}
                    borderRadius={16}
                    backgroundColor="$primary"
                    alignItems="center"
                    justifyContent="center"
                    style={{ boxShadow: cardShadow }}
                  >
                    <Camera size={20} color="#FFFFFF" />
                  </YStack>
                  <Text fontSize="$3" fontWeight="$7" textAlign="center">
                    {t('galleryPage.promptTitle', 'Füge dein nächstes Foto hinzu')}
                  </Text>
                  <Text fontSize="$2" color="$color" opacity={0.7} textAlign="center">
                    {t('galleryPage.promptDescription', 'Teile den Moment mit allen Gästen.')}
                  </Text>
                </YStack>
              </PhotoFrameTile>
            </Button>
          </YStack>
        ) : (
          <XStack gap="$3">
            <YStack flex={1} gap="$3">
              {(loading ? Array.from({ length: 5 }, (_, index) => index) : leftColumn).map((tile, index) => {
                if (typeof tile === 'number') {
                  return <PhotoFrameTile key={`left-${tile}`} height={140 + (index % 3) * 24} shimmer shimmerDelayMs={200 + index * 120} />;
                }
                const altText = t('galleryPage.photo.alt', { id: tile.id, suffix: '' }, `Foto ${tile.id}`);
                return (
                  <Button
                    key={tile.id}
                    unstyled
                    onPress={() => openLightbox(tile.id)}
                  >
                    <PhotoFrameTile height={140 + (index % 3) * 24}>
                      <YStack flex={1} width="100%" height="100%" alignItems="center" justifyContent="center">
                        <img
                          src={tile.imageUrl}
                          alt={altText}
                          loading={index < 4 ? 'eager' : 'lazy'}
                          decoding="async"
                          style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                        />
                      </YStack>
                    </PhotoFrameTile>
                  </Button>
                );
              })}
            </YStack>
            <YStack flex={1} gap="$3">
              {(loading ? Array.from({ length: 5 }, (_, index) => index) : rightColumn).map((tile, index) => {
                if (typeof tile === 'number') {
                  return <PhotoFrameTile key={`right-${tile}`} height={120 + (index % 3) * 28} shimmer shimmerDelayMs={260 + index * 140} />;
                }
                const altText = t('galleryPage.photo.alt', { id: tile.id, suffix: '' }, `Foto ${tile.id}`);
                return (
                  <Button
                    key={tile.id}
                    unstyled
                    onPress={() => openLightbox(tile.id)}
                  >
                    <PhotoFrameTile height={120 + (index % 3) * 28}>
                      <YStack flex={1} width="100%" height="100%" alignItems="center" justifyContent="center">
                        <img
                          src={tile.imageUrl}
                          alt={altText}
                          loading={index < 4 ? 'eager' : 'lazy'}
                          decoding="async"
                          style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                        />
                      </YStack>
                    </PhotoFrameTile>
                  </Button>
                );
              })}
              {!loading && rightColumn.length === 0 ? (
                <Button unstyled onPress={() => navigate(uploadPath)}>
                  <PhotoFrameTile height={180}>
                    <YStack flex={1} alignItems="center" justifyContent="center" gap="$2" padding="$3">
                      <YStack
                        width={46}
                        height={46}
                        borderRadius={16}
                        backgroundColor="$primary"
                        alignItems="center"
                        justifyContent="center"
                        style={{ boxShadow: cardShadow }}
                      >
                        <Camera size={20} color="#FFFFFF" />
                      </YStack>
                      <Text fontSize="$3" fontWeight="$7" textAlign="center">
                        {t('galleryPage.promptTitle', 'Füge dein nächstes Foto hinzu')}
                      </Text>
                      <Text fontSize="$2" color="$color" opacity={0.7} textAlign="center">
                        {t('galleryPage.promptDescription', 'Teile den Moment mit allen Gästen.')}
                      </Text>
                    </YStack>
                  </PhotoFrameTile>
                </Button>
              ) : null}
            </YStack>
          </XStack>
        )}

      </YStack>
      {lightboxOpen || lightboxMounted ? (
        <YStack
          position="fixed"
          top={0}
          left={0}
          right={0}
          bottom={0}
          zIndex={2000}
          padding="$3"
          alignItems="center"
          justifyContent="center"
          style={{
            backgroundColor: isDark
              ? `rgba(15, 23, 42, ${lightboxVisible ? 0.75 : 0})`
              : `rgba(15, 23, 42, ${lightboxVisible ? 0.45 : 0})`,
            backdropFilter: lightboxVisible ? 'blur(12px)' : 'blur(0px)',
            transition: 'background-color 220ms ease, backdrop-filter 220ms ease',
          }}
        >
          <Button
            unstyled
            onPress={closeLightbox}
            style={{ position: 'absolute', inset: 0, zIndex: 0 }}
            aria-label={t('common.actions.close', 'Close')}
          />
          <YStack width="100%" maxWidth={860} gap="$2" position="relative" style={{ zIndex: 1 }}>
            <YStack
              borderRadius="$bentoLg"
              backgroundColor={isDark ? 'rgba(15, 23, 42, 0.45)' : 'rgba(255, 255, 255, 0.65)'}
              overflow="hidden"
              position="relative"
              style={{
                height: 'min(76vh, 560px)',
                boxShadow: hardShadow,
                opacity: lightboxVisible ? 1 : 0,
                transform: lightboxVisible ? 'translateY(0) scale(1)' : 'translateY(12px) scale(0.98)',
                transition: 'transform 240ms cubic-bezier(0.22, 1, 0.36, 1), opacity 220ms ease',
              }}
            >
              <YStack flex={1} alignItems="center" justifyContent="center" padding="$2">
                {lightboxPhoto ? (
                  <YStack
                    flex={1}
                    width="100%"
                    height="100%"
                    position="relative"
                    alignItems="center"
                    justifyContent="center"
                    onTouchStart={handleTouchStart}
                    onTouchEnd={handleTouchEnd}
                    style={{ zIndex: 0 }}
                  >
                    {(() => {
                      const { from, to, direction, active } = lightboxTransition;
                      const canAnimate = Boolean(from && to && from.id !== to.id);
                      const dir = direction === 'next' ? 1 : -1;
                      const offset = `${dir * 24}%`;
                      const transition = 'transform 420ms cubic-bezier(0.22, 1, 0.36, 1), opacity 420ms ease';

                      if (!canAnimate || !to) {
                        return (
                          <img
                            src={lightboxPhoto.imageUrl}
                            alt={t('galleryPage.photo.alt', { id: lightboxPhoto.id, suffix: '' }, `Foto ${lightboxPhoto.id}`)}
                            style={{
                              width: '100%',
                              height: '100%',
                              objectFit: 'contain',
                              position: 'absolute',
                              inset: 0,
                              zIndex: 0,
                              pointerEvents: 'none',
                            }}
                          />
                        );
                      }

                      return (
                        <>
                          {from ? (
                            <img
                              src={from.imageUrl}
                              alt={t('galleryPage.photo.alt', { id: from.id, suffix: '' }, `Foto ${from.id}`)}
                              style={{
                                width: '100%',
                                height: '100%',
                                objectFit: 'contain',
                                position: 'absolute',
                                inset: 0,
                                zIndex: 0,
                                pointerEvents: 'none',
                              opacity: active ? 0 : 1,
                              transform: active ? `translateX(${dir * -24}%) scale(0.96)` : 'translateX(0) scale(1)',
                              transition,
                              willChange: 'transform, opacity',
                            }}
                          />
                          ) : null}
                          <img
                            src={to.imageUrl}
                            alt={t('galleryPage.photo.alt', { id: to.id, suffix: '' }, `Foto ${to.id}`)}
                            style={{
                              width: '100%',
                              height: '100%',
                              objectFit: 'contain',
                              position: 'absolute',
                              inset: 0,
                              zIndex: 1,
                              pointerEvents: 'none',
                              opacity: active ? 1 : 0,
                              transform: active ? 'translateX(0) scale(1)' : `translateX(${offset}) scale(0.98)`,
                              transition,
                              willChange: 'transform, opacity',
                            }}
                          />
                        </>
                      );
                    })()}
                  </YStack>
                ) : (
                  <YStack alignItems="center" gap="$2">
                    {lightboxLoading ? (
                      <>
                        <Loader2 size={24} className="animate-spin" color={isDark ? '#F8FAFF' : '#0F172A'} />
                        <Text fontSize="$2" color="$color" opacity={0.7}>
                          {t('galleryPage.loading', 'Loading…')}
                        </Text>
                      </>
                    ) : (
                      <Text fontSize="$2" color="$color" opacity={0.7}>
                        {t('lightbox.errors.notFound', 'Photo not found')}
                      </Text>
                    )}
                  </YStack>
                )}
              </YStack>
              <XStack
                position="absolute"
                top={0}
                left={0}
                right={0}
                alignItems="center"
                justifyContent="space-between"
                padding="$3"
                borderRadius={0}
                backgroundColor={isDark ? 'rgba(12, 16, 32, 0.72)' : 'rgba(255, 255, 255, 0.9)'}
                borderWidth={1}
                borderColor={mutedButtonBorder}
                style={{ backdropFilter: 'blur(10px)' }}
              >
                <YStack gap="$0.5" flexShrink={1}>
                  <Text fontSize="$1" color="$color" opacity={0.7} textTransform="uppercase" letterSpacing={1.1}>
                    {t('galleryPage.hero.label', 'Live-Galerie')}
                  </Text>
                  {lightboxPhoto?.taskId || lightboxPhoto?.taskLabel ? (
                    <XStack
                      alignItems="center"
                      gap="$1.5"
                      paddingHorizontal="$2"
                      paddingVertical="$1"
                      borderRadius="$pill"
                      borderWidth={1}
                      borderColor={lightboxPhoto.emotion?.color ?? mutedButtonBorder}
                      backgroundColor={isDark ? 'rgba(12, 16, 32, 0.55)' : 'rgba(255, 255, 255, 0.75)'}
                      style={{
                        backgroundImage: lightboxPhoto.emotion?.color
                          ? `linear-gradient(135deg, color-mix(in oklab, ${lightboxPhoto.emotion.color} 35%, transparent), color-mix(in oklab, ${lightboxPhoto.emotion.color} 10%, transparent))`
                          : undefined,
                      }}
                    >
                      {lightboxPhoto.emotion?.icon ? (
                        <Text fontSize="$2">{lightboxPhoto.emotion.icon}</Text>
                      ) : null}
                      <Text fontSize="$2" fontWeight="$6" numberOfLines={1}>
                        {lightboxPhoto.taskLabel ?? t('tasks.page.title', 'Task')}
                      </Text>
                    </XStack>
                  ) : (
                    <Text fontSize="$3" fontWeight="$7" numberOfLines={1}>
                      {event?.name ?? t('galleryPage.hero.eventFallback', 'Euer Event')}
                    </Text>
                  )}
                </YStack>
                <Button
                  size="$3"
                  circular
                  backgroundColor={mutedButton}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  onPress={closeLightbox}
                  aria-label={t('common.actions.close', 'Close')}
                >
                  <X size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                </Button>
              </XStack>
              <XStack
                position="absolute"
                bottom={0}
                left={0}
                right={0}
                alignItems="center"
                justifyContent="space-between"
                padding="$3"
                borderRadius={0}
                backgroundColor={isDark ? 'rgba(12, 16, 32, 0.7)' : 'rgba(255, 255, 255, 0.85)'}
                borderWidth={1}
                borderColor={mutedButtonBorder}
                style={{ backdropFilter: 'blur(10px)' }}
              >
                <XStack alignItems="center" gap="$2">
                  <Text fontSize="$1" color="$color" opacity={0.7}>
                    {lightboxIndex >= 0 ? `${lightboxIndex + 1} / ${displayPhotos.length}` : ''}
                  </Text>
                  {lightboxPhoto ? (
                    <XStack alignItems="center" gap="$1">
                      <Heart size={12} color={isDark ? '#F8FAFF' : '#0F172A'} />
                      <Text fontSize="$1" color="$color" opacity={0.7}>
                        {likesById[lightboxPhoto.id] ?? lightboxPhoto.likes}
                      </Text>
                    </XStack>
                  ) : null}
                </XStack>
                <XStack
                  gap="$1"
                  padding="$1"
                  borderRadius="$pill"
                  backgroundColor={mutedButton}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  alignItems="center"
                >
                  {lightboxPhoto ? (
                    <Button
                      unstyled
                      paddingHorizontal="$2.5"
                      paddingVertical="$1.5"
                      onPress={handleLike}
                      aria-label={t('galleryPage.photo.likeAria', 'Like')}
                    >
                      <Heart size={16} color={likedIds.has(lightboxPhoto.id) ? '#F43F5E' : (isDark ? '#F8FAFF' : '#0F172A')} />
                    </Button>
                  ) : null}
                  {lightboxPhoto ? (
                    <Button
                      unstyled
                      paddingHorizontal="$2"
                      paddingVertical="$1.5"
                      onPress={() => downloadPhoto(lightboxPhoto)}
                      aria-label={t('common.actions.download', 'Download')}
                    >
                      <Download size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
                    </Button>
                  ) : null}
                  {lightboxPhoto && canDelete ? (
                    <Button
                      unstyled
                      paddingHorizontal="$2"
                      paddingVertical="$1.5"
                      onPress={() => setDeleteConfirmOpen(true)}
                      disabled={deleteBusy}
                      aria-label={t('galleryPage.lightbox.deleteAria', 'Delete photo')}
                    >
                      <Trash2 size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
                    </Button>
                  ) : null}
                  <Button unstyled paddingHorizontal="$2" paddingVertical="$1.5" onPress={openShareSheet}>
                    <Share2 size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
                  </Button>
                  <Button
                    unstyled
                    paddingHorizontal="$2.5"
                    paddingVertical="$1.5"
                    onPress={() => {
                      transitionDirectionRef.current = 'prev';
                      goPrev();
                    }}
                    disabled={lightboxIndex <= 0}
                    opacity={lightboxIndex <= 0 ? 0.4 : 1}
                  >
                    <ChevronLeft size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                  </Button>
                  <Button
                    unstyled
                    paddingHorizontal="$2.5"
                    paddingVertical="$1.5"
                    onPress={() => {
                      transitionDirectionRef.current = 'next';
                      goNext();
                    }}
                    disabled={lightboxIndex < 0 || lightboxIndex >= displayPhotos.length - 1}
                    opacity={lightboxIndex < 0 || lightboxIndex >= displayPhotos.length - 1 ? 0.4 : 1}
                  >
                    <ChevronRight size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                  </Button>
                </XStack>
              </XStack>
              {deleteConfirmMounted ? (
                <YStack
                  position="absolute"
                  top={0}
                  left={0}
                  right={0}
                  bottom={0}
                  alignItems="center"
                  justifyContent="center"
                  padding="$4"
                  backgroundColor={isDark ? 'rgba(2, 6, 23, 0.7)' : 'rgba(15, 23, 42, 0.4)'}
                  opacity={deleteConfirmVisible ? 1 : 0}
                  pointerEvents={deleteConfirmVisible ? 'auto' : 'none'}
                  style={{
                    backdropFilter: 'blur(6px)',
                    zIndex: 6,
                    transition: 'opacity 200ms ease',
                  }}
                >
                  <YStack
                    width="100%"
                    maxWidth={360}
                    padding="$4"
                    gap="$3"
                    borderRadius="$card"
                    backgroundColor="$surface"
                    borderWidth={1}
                    borderColor={mutedButtonBorder}
                    opacity={deleteConfirmVisible ? 1 : 0}
                    style={{
                      boxShadow: cardShadow,
                      transform: deleteConfirmVisible
                        ? 'translate3d(0, 0, 0) scale(1)'
                        : 'translate3d(0, 12px, 0) scale(0.98)',
                      transition: 'transform 220ms cubic-bezier(0.22, 1, 0.36, 1), opacity 200ms ease',
                      willChange: 'transform, opacity',
                    }}
                  >
                    <YStack gap="$1">
                      <Text fontSize="$4" fontWeight="$7">
                        {t('galleryPage.lightbox.deleteTitle', 'Foto löschen?')}
                      </Text>
                      <Text fontSize="$2" color="$color" opacity={0.7}>
                        {t(
                          'galleryPage.lightbox.deleteDescription',
                          'Das Foto wird aus der Galerie entfernt und kann nicht wiederhergestellt werden.'
                        )}
                      </Text>
                    </YStack>
                    <XStack gap="$2" justifyContent="flex-end">
                      <Button
                        backgroundColor={mutedButton}
                        borderWidth={1}
                        borderColor={mutedButtonBorder}
                        onPress={() => setDeleteConfirmOpen(false)}
                        disabled={deleteBusy}
                      >
                        <Text fontSize="$2" fontWeight="$6">
                          {t('common.actions.cancel', 'Cancel')}
                        </Text>
                      </Button>
                      <Button
                        backgroundColor="#EF4444"
                        borderWidth={1}
                        borderColor="#EF4444"
                        onPress={handleDelete}
                        disabled={deleteBusy}
                      >
                        <XStack alignItems="center" gap="$1.5">
                          {deleteBusy ? <Loader2 size={14} className="animate-spin" color="#FFFFFF" /> : null}
                          <Text fontSize="$2" fontWeight="$7" color="#FFFFFF">
                            {deleteBusy
                              ? t('galleryPage.lightbox.deleting', 'Wird gelöscht…')
                              : t('galleryPage.lightbox.deleteConfirm', 'Foto löschen')}
                          </Text>
                        </XStack>
                      </Button>
                    </XStack>
                  </YStack>
                </YStack>
              ) : null}
              <ShareSheet
                open={shareSheet.loading || Boolean(shareSheet.url)}
                onOpenChange={(open) => {
                  if (!open) {
                    closeShareSheet();
                  }
                }}
                photoId={lightboxPhoto?.id}
                eventName={event?.name ?? null}
                url={shareSheet.url}
                loading={shareSheet.loading}
                onShareNative={() => shareNative(shareSheet.url)}
                onShareWhatsApp={() => shareWhatsApp(shareSheet.url)}
                onShareMessages={() => shareMessages(shareSheet.url)}
                onCopyLink={() => copyLink(shareSheet.url)}
                variant="inline"
              />
            </YStack>
          </YStack>
        </YStack>
      ) : null}
    </AppShell>
  );
}

function mapFullPhoto(photo: Record<string, unknown>): LightboxPhoto | null {
  const id = Number(photo.id ?? 0);
  if (!id) return null;
  const imageUrl = normalizeImageUrl(
    (photo.full_url as string | null | undefined)
      ?? (photo.file_path as string | null | undefined)
      ?? (photo.thumbnail_url as string | null | undefined)
      ?? (photo.thumbnail_path as string | null | undefined)
      ?? (photo.url as string | null | undefined)
      ?? (photo.image_url as string | null | undefined)
  );
  if (!imageUrl) return null;
  const taskLabel =
    typeof photo.task_title === 'string'
      ? photo.task_title
      : typeof photo.task_name === 'string'
        ? photo.task_name
        : typeof photo.task === 'string'
          ? photo.task
          : typeof photo.task_label === 'string'
            ? photo.task_label
            : null;
  const rawTaskId = Number(photo.task_id ?? photo.taskId ?? 0);
  const taskId = Number.isFinite(rawTaskId) && rawTaskId > 0 ? rawTaskId : null;
  const rawEmotion = (photo.emotion as Record<string, unknown> | null) ?? null;
  const emotionName =
    typeof rawEmotion?.name === 'string'
      ? rawEmotion.name
      : typeof photo.emotion_name === 'string'
        ? photo.emotion_name
        : null;
  const emotionIcon =
    typeof rawEmotion?.icon === 'string'
      ? rawEmotion.icon
      : typeof rawEmotion?.emoji === 'string'
        ? rawEmotion.emoji
        : typeof photo.emotion_icon === 'string'
          ? photo.emotion_icon
          : typeof photo.emotion_emoji === 'string'
            ? photo.emotion_emoji
            : null;
  const emotionColor =
    typeof rawEmotion?.color === 'string'
      ? rawEmotion.color
      : typeof photo.emotion_color === 'string'
        ? photo.emotion_color
        : null;
  const emotion = emotionName || emotionIcon || emotionColor
    ? { name: emotionName, icon: emotionIcon, color: emotionColor }
    : null;
  const rawIsMine = photo.is_mine ?? photo.isMine;
  const isMine = rawIsMine === true || rawIsMine === 1 || rawIsMine === '1';
  return {
    id,
    imageUrl,
    likes: typeof photo.likes_count === 'number' ? photo.likes_count : 0,
    isMine,
    taskId,
    taskLabel,
    emotion,
  };
}
