import React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { ArrowLeft, ChevronLeft, ChevronRight, Download, Heart, Share2 } from 'lucide-react';
import { useGesture } from '@use-gesture/react';
import { animated, to, useSpring } from '@react-spring/web';
import AppShell from '../components/AppShell';
import SurfaceCard from '../components/SurfaceCard';
import ShareSheet from '../components/ShareSheet';
import { useEventData } from '../context/EventDataContext';
import { fetchGallery, fetchPhoto, likePhoto, createPhotoShareLink } from '../services/photosApi';
import { useTranslation } from '@/guest/i18n/useTranslation';
import { useLocale } from '@/guest/i18n/LocaleContext';
import { useGuestThemeVariant } from '../lib/guestTheme';
import { buildEventPath } from '../lib/routes';
import { pushGuestToast } from '../lib/toast';

type LightboxPhoto = {
  id: number;
  imageUrl: string;
  likes: number;
  createdAt?: string | null;
  ingestSource?: string | 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 mapPhoto(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;
  return {
    id,
    imageUrl,
    likes: typeof photo.likes_count === 'number' ? photo.likes_count : 0,
    createdAt: typeof photo.created_at === 'string' ? photo.created_at : null,
    ingestSource: typeof photo.ingest_source === 'string' ? photo.ingest_source : null,
  };
}

export default function PhotoLightboxScreen() {
  const { token, event } = useEventData();
  const { photoId } = useParams<{ photoId: string }>();
  const navigate = useNavigate();
  const { t } = useTranslation();
  const { locale } = useLocale();
  const { isDark } = useGuestThemeVariant();
  const cardBorder = isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(15, 23, 42, 0.12)';
  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 groupBackground = isDark ? 'rgba(255, 255, 255, 0.06)' : 'rgba(15, 23, 42, 0.04)';
  const [photos, setPhotos] = React.useState<LightboxPhoto[]>([]);
  const [selectedIndex, setSelectedIndex] = React.useState<number | null>(null);
  const [cursor, setCursor] = React.useState<string | null>(null);
  const [loading, setLoading] = React.useState(false);
  const [loadingMore, setLoadingMore] = React.useState(false);
  const [error, setError] = React.useState<string | null>(null);
  const [likes, setLikes] = React.useState<Record<number, number>>({});
  const [shareSheet, setShareSheet] = React.useState<{ url: string | null; loading: boolean }>({
    url: null,
    loading: false,
  });
  const zoomContainerRef = React.useRef<HTMLDivElement | null>(null);
  const zoomImageRef = React.useRef<HTMLImageElement | null>(null);
  const baseSizeRef = React.useRef({ width: 0, height: 0 });
  const scaleRef = React.useRef(1);
  const lastTapRef = React.useRef(0);
  const [isZoomed, setIsZoomed] = React.useState(false);

  const [{ x, y, scale }, api] = useSpring(() => ({
    x: 0,
    y: 0,
    scale: 1,
    config: { tension: 260, friction: 28 },
  }));

  const selected = selectedIndex !== null ? photos[selectedIndex] : null;

  const loadPage = React.useCallback(
    async (nextCursor?: string | null, replace = false) => {
      if (!token) return { items: [], nextCursor: null };
      const response = await fetchGallery(token, { cursor: nextCursor ?? undefined, limit: 28, locale });
      const mapped = (response.data ?? [])
        .map((photo) => mapPhoto(photo as Record<string, unknown>))
        .filter(Boolean) as LightboxPhoto[];
      setCursor(response.next_cursor ?? null);
      setPhotos((prev) => (replace ? mapped : [...prev, ...mapped]));
      setLikes((prev) => {
        const next = { ...prev };
        for (const item of mapped) {
          if (next[item.id] === undefined) {
            next[item.id] = item.likes;
          }
        }
        return next;
      });
      return { items: mapped, nextCursor: response.next_cursor ?? null };
    },
    [locale, token]
  );

  React.useEffect(() => {
    if (!token || !photoId) {
      setError(t('lightbox.errors.notFound', 'Photo not found'));
      return;
    }

    let active = true;
    const targetId = Number(photoId);

    const init = async () => {
      setLoading(true);
      setError(null);
      try {
        let foundIndex = -1;
        let nextCursor: string | null = null;
        let combined: LightboxPhoto[] = [];

        for (let page = 0; page < 5; page += 1) {
          const response = await fetchGallery(token, {
            cursor: nextCursor ?? undefined,
            limit: 28,
            locale,
          });
          const mapped = (response.data ?? [])
            .map((photo) => mapPhoto(photo as Record<string, unknown>))
            .filter(Boolean) as LightboxPhoto[];
          combined = [...combined, ...mapped];
          nextCursor = response.next_cursor ?? null;
          foundIndex = combined.findIndex((item) => item.id === targetId);
          if (foundIndex >= 0 || !nextCursor) {
            break;
          }
        }

        if (!active) return;

        if (combined.length > 0) {
          setPhotos(combined);
          setCursor(nextCursor);
          setLikes((prev) => {
            const next = { ...prev };
            for (const item of combined) {
              if (next[item.id] === undefined) {
                next[item.id] = item.likes;
              }
            }
            return next;
          });
        }

        if (foundIndex >= 0) {
          setSelectedIndex(foundIndex);
          return;
        }

        const single = await fetchPhoto(targetId, locale);
        if (!active) return;
        const mappedSingle = single ? mapPhoto(single as Record<string, unknown>) : null;
        if (mappedSingle) {
          setPhotos([mappedSingle]);
          setSelectedIndex(0);
          return;
        }

        setError(t('lightbox.errors.notFound', 'Photo not found'));
      } catch (err) {
        if (!active) return;
        console.error('Photo lightbox load failed', err);
        setError(t('lightbox.errors.loadFailed', 'Failed to load photo'));
      } finally {
        if (active) setLoading(false);
      }
    };

    init();

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

  const updateBaseSize = React.useCallback(() => {
    if (!zoomImageRef.current) {
      return;
    }
    const rect = zoomImageRef.current.getBoundingClientRect();
    baseSizeRef.current = { width: rect.width, height: rect.height };
  }, []);

  React.useEffect(() => {
    updateBaseSize();
  }, [selected?.id, updateBaseSize]);

  React.useEffect(() => {
    window.addEventListener('resize', updateBaseSize);
    return () => window.removeEventListener('resize', updateBaseSize);
  }, [updateBaseSize]);

  const clamp = React.useCallback((value: number, min: number, max: number) => {
    return Math.min(max, Math.max(min, value));
  }, []);

  const getBounds = React.useCallback(
    (nextScale: number) => {
      const container = zoomContainerRef.current?.getBoundingClientRect();
      const { width, height } = baseSizeRef.current;
      if (!container || !width || !height) {
        return { maxX: 0, maxY: 0 };
      }
      const scaledWidth = width * nextScale;
      const scaledHeight = height * nextScale;
      const maxX = Math.max(0, (scaledWidth - container.width) / 2);
      const maxY = Math.max(0, (scaledHeight - container.height) / 2);
      return { maxX, maxY };
    },
    []
  );

  const resetZoom = React.useCallback(() => {
    scaleRef.current = 1;
    setIsZoomed(false);
    api.start({ x: 0, y: 0, scale: 1 });
  }, [api]);

  React.useEffect(() => {
    resetZoom();
  }, [selected?.id, resetZoom]);

  const toggleZoom = React.useCallback(() => {
    const nextScale = scaleRef.current > 1.01 ? 1 : 2;
    scaleRef.current = nextScale;
    setIsZoomed(nextScale > 1.01);
    api.start({ x: 0, y: 0, scale: nextScale });
  }, [api]);

  const goPrev = React.useCallback(() => {
    if (selectedIndex === null || selectedIndex <= 0) return;
    setSelectedIndex(selectedIndex - 1);
  }, [selectedIndex]);

  const goNext = React.useCallback(async () => {
    if (selectedIndex === null) return;
    if (selectedIndex < photos.length - 1) {
      setSelectedIndex(selectedIndex + 1);
      return;
    }
    if (!cursor || loadingMore) {
      return;
    }
    setLoadingMore(true);
    try {
      const { items } = await loadPage(cursor);
      if (items.length > 0) {
        setSelectedIndex((prev) => (typeof prev === 'number' ? prev + 1 : prev));
      }
    } finally {
      setLoadingMore(false);
    }
  }, [cursor, loadPage, loadingMore, photos.length, selectedIndex]);

  const handleLike = React.useCallback(async () => {
    if (!selected || !token) return;
    setLikes((prev) => ({ ...prev, [selected.id]: (prev[selected.id] ?? selected.likes) + 1 }));
    try {
      const count = await likePhoto(selected.id);
      setLikes((prev) => ({ ...prev, [selected.id]: count }));
    } catch (error) {
      console.error('Like failed', error);
    }
  }, [selected, 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 (!selected || !token) return;
    setShareSheet({ url: null, loading: true });
    try {
      const payload = await createPhotoShareLink(token, selected.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 });
    }
  }, [selected, 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((url?: string | null, id?: number | null) => {
    if (!url || !id) return;
    const link = document.createElement('a');
    link.href = url;
    link.download = `photo-${id}.jpg`;
    link.rel = 'noreferrer';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }, []);

  const bind = useGesture(
    {
      onDrag: ({ down, movement: [mx, my], offset: [ox, oy], last, event }) => {
        if (event.cancelable) {
          event.preventDefault();
        }
        const zoomed = scaleRef.current > 1.01;
        if (!zoomed) {
          api.start({ x: down ? mx : 0, y: 0, immediate: down });
          if (last) {
            api.start({ x: 0, y: 0, immediate: false });
            const threshold = 80;
            if (Math.abs(mx) > threshold) {
              if (mx > 0) {
                goPrev();
              } else {
                void goNext();
              }
            }
          }
          return;
        }

        const { maxX, maxY } = getBounds(scaleRef.current);
        api.start({
          x: clamp(ox, -maxX, maxX),
          y: clamp(oy, -maxY, maxY),
          immediate: down,
        });
      },
      onPinch: ({ offset: [nextScale], last, event }) => {
        if (event.cancelable) {
          event.preventDefault();
        }
        const clampedScale = clamp(nextScale, 1, 3);
        scaleRef.current = clampedScale;
        setIsZoomed(clampedScale > 1.01);
        const { maxX, maxY } = getBounds(clampedScale);
        api.start({
          scale: clampedScale,
          x: clamp(x.get(), -maxX, maxX),
          y: clamp(y.get(), -maxY, maxY),
          immediate: true,
        });
        if (last && clampedScale <= 1.01) {
          resetZoom();
        }
      },
    },
    {
      drag: {
        from: () => [x.get(), y.get()],
        filterTaps: true,
        threshold: 4,
      },
      pinch: {
        scaleBounds: { min: 1, max: 3 },
        rubberband: true,
      },
      eventOptions: { passive: false },
    }
  );

  const handlePointerUp = (event: React.PointerEvent) => {
    if (event.pointerType !== 'touch') {
      return;
    }
    const now = Date.now();
    if (now - lastTapRef.current < 280) {
      lastTapRef.current = 0;
      toggleZoom();
      return;
    }
    lastTapRef.current = now;
  };

  return (
    <AppShell>
      <YStack gap="$4">
        <SurfaceCard position="relative">
          <XStack alignItems="center" justifyContent="space-between">
            <Button
              size="$3"
              borderRadius="$pill"
              backgroundColor={mutedButton}
              borderWidth={1}
              borderColor={mutedButtonBorder}
              onPress={() => navigate(buildEventPath(token, '/gallery'))}
              paddingHorizontal="$3"
              aria-label={t('common.actions.close', 'Close')}
            >
              <ArrowLeft size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
            </Button>
            <Text fontSize="$4" fontWeight="$7">
              {t('galleryPage.title', 'Gallery')}
            </Text>
            <XStack width={48} />
          </XStack>
        </SurfaceCard>

        <SurfaceCard padding="$4">
          {loading ? (
            <Text fontSize="$3" color="$color" opacity={0.7}>
              {t('galleryPage.loading', 'Loading…')}
            </Text>
          ) : error ? (
            <Text fontSize="$3" color="#FCA5A5">
              {error}
            </Text>
          ) : selected ? (
            <YStack gap="$3">
              <YStack
                borderRadius="$card"
                backgroundColor="$muted"
                borderWidth={1}
                borderColor={cardBorder}
                overflow="hidden"
                alignItems="center"
                justifyContent="center"
                style={{ height: 'min(70vh, 520px)' }}
              >
                <div
                  ref={zoomContainerRef}
                  style={{
                    width: '100%',
                    height: '100%',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    touchAction: 'none',
                  }}
                  {...bind()}
                  onPointerUp={handlePointerUp}
                >
                  <animated.div
                    style={{
                      transform: to([x, y, scale], (nx, ny, ns) => `translate3d(${nx}px, ${ny}px, 0) scale(${ns})`),
                      width: '100%',
                      height: '100%',
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                    }}
                  >
                    <img
                      ref={zoomImageRef}
                      src={selected.imageUrl}
                      alt={t('galleryPage.photo.alt', { id: selected.id }, 'Photo {id}')}
                      onLoad={updateBaseSize}
                      style={{
                        width: '100%',
                        height: '100%',
                        objectFit: 'contain',
                        userSelect: 'none',
                        pointerEvents: isZoomed ? 'auto' : 'none',
                      }}
                    />
                  </animated.div>
                </div>
              </YStack>
              <XStack alignItems="center" justifyContent="space-between">
                <Text fontSize="$2" color="$color" opacity={0.7}>
                  {selectedIndex !== null ? `${selectedIndex + 1} / ${photos.length}` : ''}
                </Text>
                <XStack
                  gap="$1"
                  padding="$1"
                  borderRadius="$pill"
                  backgroundColor={groupBackground}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  alignItems="center"
                  flexWrap="wrap"
                  justifyContent="flex-end"
                >
                  <Button
                    unstyled
                    disabled={selectedIndex === null || selectedIndex <= 0}
                    onPress={goPrev}
                    paddingHorizontal="$3"
                    paddingVertical="$2"
                    opacity={selectedIndex === null || selectedIndex <= 0 ? 0.4 : 1}
                  >
                    <XStack alignItems="center" gap="$2">
                      <ChevronLeft size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                      <Text fontSize="$2" fontWeight="$6">
                        {t('galleryPage.lightbox.prev', 'Prev')}
                      </Text>
                    </XStack>
                  </Button>
                  <Button
                    unstyled
                    disabled={selectedIndex === null || (selectedIndex >= photos.length - 1 && !cursor)}
                    onPress={goNext}
                    paddingHorizontal="$3"
                    paddingVertical="$2"
                    opacity={selectedIndex === null || (selectedIndex >= photos.length - 1 && !cursor) ? 0.4 : 1}
                  >
                    <XStack alignItems="center" gap="$2">
                      <Text fontSize="$2" fontWeight="$6">
                        {loadingMore ? t('common.actions.loading', 'Loading...') : t('galleryPage.lightbox.next', 'Next')}
                      </Text>
                      <ChevronRight size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                    </XStack>
                  </Button>
                </XStack>
              </XStack>
              <XStack alignItems="center" justifyContent="space-between">
                <Text fontSize="$4" fontWeight="$7">
                  {t('galleryPage.lightbox.likes', { count: likes[selected.id] ?? selected.likes }, '{count} likes')}
                </Text>
                <XStack
                  gap="$1"
                  padding="$1"
                  borderRadius="$pill"
                  backgroundColor={groupBackground}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  alignItems="center"
                  flexWrap="wrap"
                  justifyContent="flex-end"
                >
                  <Button
                    unstyled
                    onPress={() => downloadPhoto(selected?.imageUrl ?? null, selected?.id ?? null)}
                    paddingHorizontal="$3"
                    paddingVertical="$2"
                  >
                    <XStack alignItems="center" gap="$2">
                      <Download size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                      <Text fontSize="$2" fontWeight="$6">
                        {t('common.actions.download', 'Download')}
                      </Text>
                    </XStack>
                  </Button>
                  <Button
                    unstyled
                    onPress={handleLike}
                    paddingHorizontal="$3"
                    paddingVertical="$2"
                  >
                    <XStack alignItems="center" gap="$2">
                      <Heart size={16} color="#FFFFFF" />
                      <Text fontSize="$2" fontWeight="$6" color="#FFFFFF">
                        {t('galleryPage.photo.likeAria', 'Like')}
                      </Text>
                    </XStack>
                  </Button>
                  <Button
                    unstyled
                    onPress={openShareSheet}
                    paddingHorizontal="$3"
                    paddingVertical="$2"
                  >
                    <XStack alignItems="center" gap="$2">
                      <Share2 size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
                      <Text fontSize="$2" fontWeight="$6">
                        {shareSheet.loading ? t('share.loading', 'Sharing...') : t('share.button', 'Share')}
                      </Text>
                    </XStack>
                  </Button>
                </XStack>
              </XStack>
            </YStack>
          ) : (
            <Text fontSize="$3" color="$color" opacity={0.7}>
              {t('lightbox.errors.notFound', 'Photo not found')}
            </Text>
          )}
          <ShareSheet
            open={shareSheet.loading || Boolean(shareSheet.url)}
            onOpenChange={(open) => {
              if (!open) {
                closeShareSheet();
              }
            }}
            photoId={selected?.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"
          />
        </SurfaceCard>
      </YStack>
    </AppShell>
  );
}
