import React, { useMemo } from 'react'

import CarouselNav from './CarouselNav'
import DownloadAction from './DownloadAction'
import { filenameFromUrl } from './fileMeta'
import { useCarousel } from './useCarousel'
import ViewerShell from './ViewerShell'

export interface ImageViewerItem {
  src: string
  alt?: string
  /**
   * Filename used by the download action and as the dialog's
   * accessible name. Falls back to the last pathname segment of
   * `src` when omitted.
   */
  filename?: string
}

export interface ImageViewerProps {
  open: boolean
  items: ImageViewerItem[]
  /** Index to display when the viewer first opens. Defaults to `0`. */
  initialIndex?: number
  onClose: () => void
}

/**
 * Native lightbox-style image viewer — full-viewport `<dialog>` with
 * the image centered at reasonable max dimensions, a single close
 * button, and a download action in the chrome.
 *
 * When `items.length > 1` the viewer becomes a carousel:
 *
 *   - Prev / next on-screen controls hug the viewport sides.
 *   - `ArrowLeft` / `ArrowRight` step between siblings (the keyboard
 *     listener is parked while a `<video>` / `<audio>` element is
 *     focused so native media seek still works in mixed contexts).
 *   - A `current / total` counter sits in the top-left chrome.
 *
 * Used by every `MessageAttachment.Image.*` variant
 * (Composer / Sent / Received) so admins can preview attachments
 * before sending and recipients can preview after receipt.
 */
const ImageViewer: React.FC<ImageViewerProps> = ({
  open,
  items,
  initialIndex = 0,
  onClose,
}) => {
  const { index, prev, next } = useCarousel({
    length: items.length,
    initialIndex,
    open,
  })

  const item = items[index]
  const filename = useMemo(
    () => item?.filename ?? (item ? filenameFromUrl(item.src) : 'image'),
    [item]
  )

  if (!item) return null

  return (
    <ViewerShell
      open={open}
      onClose={onClose}
      ariaLabel={filename}
      counter={items.length > 1 ? `${index + 1} / ${items.length}` : undefined}
      actions={
        <DownloadAction
          url={item.src}
          filename={filename}
          variant="viewer"
          label={`Download ${filename}`}
        />
      }
      data-testid="image-viewer"
    >
      {open ? (
        <img
          // Forcing a key swap on item change ensures React replaces
          // the `<img>` cleanly between siblings — otherwise the
          // previous image stays painted for a frame while the new
          // `src` decodes, which reads as a stutter at carousel pace.
          key={`${index}:${item.src}`}
          src={item.src}
          alt={item.alt ?? filename}
          draggable={false}
          // Gated on `open` (see the `{open ? … : null}` above): `ViewerShell`
          // keeps its `<dialog>` mounted across open/close so the platform
          // transition can play, but a mounted-closed dialog would still hold
          // this `<img>` in the DOM. The viewer uses the raw `item.src` — the
          // full-resolution original, not the CDN-optimised URL the bubble tile
          // renders — so rendering it unconditionally made every image in a
          // thread prefetch its full-res original. Only mount it while open.
          decoding="async"
          className="block h-auto max-h-[calc(100dvh-128px)] w-auto max-w-[min(96vw,1400px)] select-none object-contain"
        />
      ) : null}

      {items.length > 1 ? (
        <CarouselNav
          onPrev={prev}
          onNext={next}
          prevLabel="Previous image"
          nextLabel="Next image"
        />
      ) : null}
    </ViewerShell>
  )
}

export default ImageViewer
