// Reusable status-media block for non-feed surfaces. The feed renders
// `PostMedia` / `PostAlbumMedia` at full window width with reblog-aware
// borders; everywhere else (thread comments, quoted statuses) wants the
// same per-status branching (single image / video / album with dots) but
// at a caller-controlled width and height cap.
//
// Branches:
//   - 0 attachments       → null
//   - sensitive + hidden  → blurhash backdrop + "Tap to view"
//   - video / gifv        → VideoPlayer (shares the screen's PlaybackScope)
//   - 1 image             → expo-image at intrinsic aspect, lightbox on tap
//   - 2+ images           → shared `MediaCarousel` (dots / count pill)
//
// Every media helper (`isVideoPost`, `toImageUri`, `toVideoSource`,
// `mediaHeight`) is reused from `src/lib/media-source.ts` so the
// federated-image fallback, aspect→height sizing, and HLS detection stay in
// lockstep with the feed renderer. The album carousel (`MediaCarousel`) and
// sensitive shroud (`SensitiveShroud`, `card` variant) are likewise shared
// with the feed renderer.

import { Image } from 'expo-image'
import { useCallback, useMemo, useState } from 'react'
import { Pressable } from 'react-native'
import { useSharedValue } from 'react-native-reanimated'
import MediaCarousel, {
  type MediaCarouselSlide,
} from 'src/components/common/media/MediaCarousel'
import SensitiveShroud from 'src/components/common/media/SensitiveShroud'
import SaveableMedia from 'src/components/common/SaveableMedia'
import VideoPlayer from 'src/components/post/VideoPlayer'
import { useImageViewer } from 'src/hooks/useImageViewer'
import type { MediaAttachment, Status } from 'src/lib/api-types'
import {
  getIntrinsicAspectRatio,
  isVideoPost,
  mediaHeight,
  toImageUri,
  toVideoSource,
} from 'src/lib/media-source'
import { View } from 'tamagui'

type Props = {
  status: Pick<Status, 'id' | 'sensitive' | 'media_attachments' | 'pf_type'>
  // Caller-provided pixel width. Carousel and video need an explicit number.
  width: number
  // Optional ceiling so portrait media doesn't dominate a narrow container.
  maxHeight?: number
  borderRadius?: number
}

const DEFAULT_RADIUS = 10

// Discard-gesture wrapper around `Pressable` that doesn't fight the
// carousel pan; tap fires only when the gesture detector hasn't claimed
// the touch as a swipe.
function SlideTap({
  onPress,
  children,
}: {
  onPress: () => void
  children: React.ReactNode
}) {
  return (
    <Pressable onPress={onPress} hitSlop={0}>
      {children}
    </Pressable>
  )
}

export default function StatusMediaPreview({
  status,
  width,
  maxHeight,
  borderRadius = DEFAULT_RADIUS,
}: Props) {
  const media = status.media_attachments
  const { open: openImageViewer } = useImageViewer()
  const [revealed, setRevealed] = useState(false)
  const progress = useSharedValue<number>(0)

  if (!media || media.length === 0) return null

  // Sensitive shroud — `card` variant: compact, rounded, tap-anywhere to
  // reveal (no competing gesture on embedded cards). The feed's full-bleed,
  // dual-separator variant lives behind the same component.
  if (status.sensitive && !revealed) {
    const height = mediaHeight(media[0], width, { maxHeight })
    return (
      <SensitiveShroud
        variant="card"
        width={width}
        height={height}
        blurhash={media[0]?.blurhash}
        borderRadius={borderRadius}
        onReveal={() => setRevealed(true)}
      />
    )
  }

  // Video / gifv branch. VideoPlayer sizes itself from the server aspect (or
  // its measured poster aspect when dims are missing), clamped at maxHeight so
  // a portrait video in a quoted-status card can't dominate. The wrapper just
  // rounds + clips the corners; it shrink-wraps to the player's own height.
  if (isVideoPost(status)) {
    const v = media[0]
    return (
      <View width={width} borderRadius={borderRadius} overflow="hidden">
        <SaveableMedia attachment={v} kind="video">
          <VideoPlayer
            source={toVideoSource(v)}
            width={width}
            aspectRatio={getIntrinsicAspectRatio(v) ?? undefined}
            maxHeight={maxHeight}
            videoId={status.id}
            previewUrl={v.preview_url}
            blurhash={v.blurhash}
          />
        </SaveableMedia>
      </View>
    )
  }

  // Single image branch.
  if (media.length === 1) {
    const item = media[0]
    const height = mediaHeight(item, width, { maxHeight })
    return (
      <SaveableMedia attachment={item} kind="image">
        <Pressable
          hitSlop={4}
          onPress={() =>
            openImageViewer({
              uri: toImageUri(item),
              blurhash: item.blurhash ?? undefined,
              width: item.meta?.original?.width,
              height: item.meta?.original?.height,
            })
          }
        >
          <Image
            source={{ uri: toImageUri(item) }}
            placeholder={item.blurhash ? { blurhash: item.blurhash } : undefined}
            placeholderContentFit="cover"
            contentFit="cover"
            cachePolicy="memory-disk"
            style={{ width, height, borderRadius, backgroundColor: '#000' }}
          />
        </Pressable>
      </SaveableMedia>
    )
  }

  return (
    <AlbumPreview
      media={media}
      postId={status.id}
      width={width}
      maxHeight={maxHeight}
      borderRadius={borderRadius}
      progress={progress}
      onSlidePress={(idx) => {
        const item = media[idx] ?? media[0]
        openImageViewer({
          uri: toImageUri(item),
          blurhash: item.blurhash ?? undefined,
          width: item.meta?.original?.width,
          height: item.meta?.original?.height,
        })
      }}
    />
  )
}

type AlbumProps = {
  media: MediaAttachment[]
  postId: string
  width: number
  maxHeight?: number
  borderRadius: number
  progress: ReturnType<typeof useSharedValue<number>>
  onSlidePress: (index: number) => void
}

function AlbumPreview({
  media,
  postId,
  width,
  maxHeight,
  borderRadius,
  progress,
  onSlidePress,
}: AlbumProps) {
  // Mirror PostAlbumMedia: aspect from the first slide, not the tallest.
  // Mixed-aspect albums letterbox via `contentFit="contain"` inside slides.
  const height = useMemo(
    () => mediaHeight(media[0], width, { maxHeight }),
    [media, width, maxHeight]
  )

  // Embedded cards have no competing post-cell gesture, so each slide carries
  // its own press target (SlideTap → lightbox) — unlike the feed's bare slides.
  const renderSlide = useCallback(
    ({ item, index, width, height }: MediaCarouselSlide) => (
      <SaveableMedia attachment={item} kind="image">
        <SlideTap onPress={() => onSlidePress(index)}>
          <Image
            source={{ uri: toImageUri(item) }}
            placeholder={item.blurhash ? { blurhash: item.blurhash } : undefined}
            placeholderContentFit="cover"
            contentFit="contain"
            cachePolicy="memory-disk"
            style={{ width, height, backgroundColor: '#000' }}
            recyclingKey={`${postId}:${index}`}
          />
        </SlideTap>
      </SaveableMedia>
    ),
    [postId, onSlidePress]
  )

  return (
    <MediaCarousel
      media={media}
      width={width}
      height={height}
      progress={progress}
      renderSlide={renderSlide}
      borderRadius={borderRadius}
    />
  )
}
