import React, { useCallback, useMemo, useState } from 'react';
import { Image, Modal, PanResponder, Pressable, Text, View } from 'react-native';
import { ChevronLeft, ChevronRight, ImageOff, X } from 'lucide-react-native';

import { imageGalleryStyles } from './imageGalleryStyles';
import { themedColor } from '../theme';

// Horizontal travel (px) past which a lightbox swipe pages to the next/prev image.
const SWIPE_THRESHOLD = 50;

export type GalleryImage = {
  /** Stable key for React lists (and for tracking the active lightbox image). */
  key: string;
  /** Public image URL once resolved; null/undefined while loading or on failure. */
  url?: string | null;
  /** Source file name, used for alt text and the failed-tile label. */
  fileName?: string;
  /** True while the image is still resolving (renders a placeholder tile). */
  loading?: boolean;
  /** True once the image failed to resolve (renders a failed tile). */
  failed?: boolean;
};

/** Generic image gallery: a single resolved image renders inline (full width,
 *  natural aspect ratio); a batch collapses into a 2-column grid. Tapping a
 *  resolved image opens a full-screen lightbox that pages through the resolved
 *  images (swipe or arrows). Loading/failed images render placeholder tiles.
 *
 *  Shared by the `send_image` tool renderer (`SendImageGallery`) and the
 *  image-attachment preview in chat messages. */
export function ImageGallery({ images, onOpen }: { images: GalleryImage[]; onOpen?: (image: GalleryImage) => void }) {
  const viewable = useMemo(() => images.filter((img) => img.url), [images]);
  // Track the active lightbox image by key, not index: as earlier images in a
  // batch resolve they shift positions, so a stored index would silently point
  // at a different image. The key is stable.
  const [activeKey, setActiveKey] = useState<string | null>(null);

  const closeLightbox = useCallback(() => setActiveKey(null), []);
  // Step to the prev/next resolved image by key (no-op at the ends).
  const stepLightbox = useCallback(
    (delta: number) =>
      setActiveKey((key) => {
        if (key == null) return key;
        const next = viewable[viewable.findIndex((v) => v.key === key) + delta];
        return next ? next.key : key;
      }),
    [viewable],
  );
  const showPrev = useCallback(() => stepLightbox(-1), [stepLightbox]);
  const showNext = useCallback(() => stepLightbox(1), [stepLightbox]);

  // Swipe left/right inside the lightbox to page through images. Claim the
  // gesture only for deliberate horizontal drags so a plain tap still bubbles to
  // the backdrop (which closes) and vertical motion is ignored.
  const panResponder = useMemo(
    () =>
      PanResponder.create({
        onMoveShouldSetPanResponder: (_evt, g) =>
          Math.abs(g.dx) > 12 && Math.abs(g.dx) > Math.abs(g.dy) * 1.5,
        onPanResponderRelease: (_evt, g) => {
          if (g.dx <= -SWIPE_THRESHOLD) showNext();
          else if (g.dx >= SWIPE_THRESHOLD) showPrev();
        },
      }),
    [showPrev, showNext],
  );

  if (images.length === 0) return null;

  // Only the resolved single-image case uses the full-width SingleImage; a lone
  // loading/failed image falls through to the tile path, which renders a proper
  // placeholder instead of an Image with a null URI.
  const isSingle = images.length === 1 && !!images[0].url;
  const openLightbox = (img: GalleryImage) => {
    onOpen?.(img);
    setActiveKey(img.key);
  };
  // Derive the active image and its position from the key each render, so the
  // lightbox stays on the same image as `viewable` grows/reorders. A key that's
  // no longer viewable yields a null active, which closes the Modal.
  const activeIdx = activeKey != null ? viewable.findIndex((v) => v.key === activeKey) : -1;
  const active = activeIdx >= 0 ? viewable[activeIdx] : null;
  const canGoPrev = activeIdx > 0;
  const canGoNext = activeIdx >= 0 && activeIdx < viewable.length - 1;

  return (
    <View>
      {isSingle ? (
        <SingleImage image={images[0]} onPress={() => openLightbox(images[0])} />
      ) : (
        <View style={imageGalleryStyles.sendImageGrid}>
          {images.map((img) => (
            // GalleryTile only renders a pressable for resolved images, so the
            // handler is a no-op for loading/failed tiles.
            <GalleryTile image={img} key={img.key} onPress={() => openLightbox(img)} />
          ))}
        </View>
      )}

      <Modal
        animationType="fade"
        onRequestClose={closeLightbox}
        transparent
        visible={active !== null}
      >
        <Pressable onPress={closeLightbox} style={imageGalleryStyles.sendImageLightboxBackdrop}>
          {active ? (
            <View {...panResponder.panHandlers} style={imageGalleryStyles.sendImageLightboxSwipeArea}>
              <Image
                accessibilityLabel={active.fileName || undefined}
                accessibilityRole="image"
                resizeMode="contain"
                source={{ uri: active.url as string }}
                style={imageGalleryStyles.sendImageLightboxImage}
              />
            </View>
          ) : null}

          <Pressable
            accessibilityLabel="Close"
            accessibilityRole="button"
            onPress={closeLightbox}
            style={imageGalleryStyles.sendImageLightboxClose}
          >
            <X color="#FFFFFF" size={22} strokeWidth={2.2} />
          </Pressable>

          {canGoPrev ? (
            <Pressable
              accessibilityLabel="Previous image"
              accessibilityRole="button"
              onPress={showPrev}
              style={[imageGalleryStyles.sendImageLightboxNav, { left: 12 }]}
            >
              <ChevronLeft color="#FFFFFF" size={26} strokeWidth={2.2} />
            </Pressable>
          ) : null}

          {canGoNext ? (
            <Pressable
              accessibilityLabel="Next image"
              accessibilityRole="button"
              onPress={showNext}
              style={[imageGalleryStyles.sendImageLightboxNav, { right: 12 }]}
            >
              <ChevronRight color="#FFFFFF" size={26} strokeWidth={2.2} />
            </Pressable>
          ) : null}

          {viewable.length > 1 ? (
            <Text style={imageGalleryStyles.sendImageLightboxCounter}>
              {activeIdx + 1} / {viewable.length}
            </Text>
          ) : null}
        </Pressable>
      </Modal>
    </View>
  );
}

function SingleImage({ image, onPress }: { image: GalleryImage; onPress: () => void }) {
  // Lock the aspect ratio once the natural size loads (falls back to 3:2).
  const [aspectRatio, setAspectRatio] = useState<number | null>(null);
  return (
    <Pressable
      accessibilityLabel={image.fileName ? `Open ${image.fileName}` : 'Open image'}
      accessibilityRole="button"
      onPress={onPress}
      style={imageGalleryStyles.sendImageSingle}
    >
      <Image
        accessibilityLabel={image.fileName || undefined}
        accessibilityRole="image"
        onLoad={(event) => {
          const { height, width } = event.nativeEvent.source;
          if (width > 0 && height > 0) setAspectRatio(width / height);
        }}
        resizeMode="contain"
        source={{ uri: image.url as string }}
        style={[imageGalleryStyles.sendImageSingleImage, { aspectRatio: aspectRatio ?? 1.5 }]}
      />
    </Pressable>
  );
}

function GalleryTile({ image, onPress }: { image: GalleryImage; onPress?: () => void }) {
  if (image.loading) {
    return <View style={[imageGalleryStyles.sendImageTile, imageGalleryStyles.sendImageTileLoading]} />;
  }

  if (image.failed || !image.url) {
    return (
      <View style={[imageGalleryStyles.sendImageTile, imageGalleryStyles.sendImageTileFailed]}>
        <ImageOff color={themedColor('#FCA5A5')} size={18} strokeWidth={2.2} />
        <Text numberOfLines={1} style={imageGalleryStyles.sendImageFailedText}>
          {image.fileName || 'Image failed'}
        </Text>
      </View>
    );
  }

  return (
    <Pressable
      accessibilityLabel={image.fileName ? `Open ${image.fileName}` : 'Open image'}
      accessibilityRole="button"
      onPress={onPress}
      style={imageGalleryStyles.sendImageTile}
    >
      <Image
        accessibilityLabel={image.fileName || undefined}
        accessibilityRole="image"
        resizeMode="cover"
        source={{ uri: image.url }}
        style={imageGalleryStyles.sendImageTileImage}
      />
    </Pressable>
  );
}
