"use client";

import {
  type CSSProperties,
  type PointerEvent as ReactPointerEvent,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import Image from "next/image";
import { ChevronLeft, ChevronRight, X } from "lucide-react";

import type { PrivateAlbum } from "@/data/schemas/private-room";

import {
  getResistedGalleryDragDistance,
  resolveGallerySwipeDirection,
} from "../private-album-gallery-motion";
import { getPrivateAlbumDisplayImages } from "../private-album-images";
import styles from "../private-room-screen.module.css";

export interface PrivateAlbumGalleryProps {
  album: PrivateAlbum;
  imageIndex: number;
  onClose: () => void;
  onImageIndexChange: (index: number) => void;
}

interface ActivePointerGesture {
  pointerId: number;
  startX: number;
  startTime: number;
  startPosition: number;
  viewportWidth: number;
}

type GallerySlideStyle = CSSProperties & {
  "--gallery-slide-offset": string;
  "--gallery-drag-offset": string;
};

export function PrivateAlbumGallery({
  album,
  imageIndex,
  onClose,
  onImageIndexChange,
}: PrivateAlbumGalleryProps) {
  const displayImages = useMemo(
    () => getPrivateAlbumDisplayImages(album),
    [album],
  );
  const [activeSourceIndex, setActiveSourceIndex] = useState(imageIndex);
  const [dragOffset, setDragOffset] = useState(0);
  const [isDragging, setIsDragging] = useState(false);
  const gestureRef = useRef<ActivePointerGesture | null>(null);
  const activePosition = displayImages.findIndex(
    (image) => image.sourceIndex === activeSourceIndex,
  );
  const currentImage = displayImages[activePosition];
  const hasPrevious = activePosition > 0;
  const hasNext =
    activePosition >= 0 && activePosition < displayImages.length - 1;

  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") onClose();
      if (event.key === "ArrowLeft" && hasPrevious) {
        const previousImage = displayImages[activePosition - 1];
        if (previousImage) {
          setActiveSourceIndex(previousImage.sourceIndex);
          onImageIndexChange(previousImage.sourceIndex);
        }
      }
      if (event.key === "ArrowRight" && hasNext) {
        const nextImage = displayImages[activePosition + 1];
        if (nextImage) {
          setActiveSourceIndex(nextImage.sourceIndex);
          onImageIndexChange(nextImage.sourceIndex);
        }
      }
    };
    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [
    activePosition,
    displayImages,
    hasNext,
    hasPrevious,
    onClose,
    onImageIndexChange,
  ]);

  if (!currentImage) return null;

  const changeToPosition = (position: number) => {
    const nextImage = displayImages[position];
    if (!nextImage || position === activePosition) return;
    setActiveSourceIndex(nextImage.sourceIndex);
    setDragOffset(0);
    setIsDragging(false);
    onImageIndexChange(nextImage.sourceIndex);
  };

  const handlePointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {
    if (event.button !== 0 || displayImages.length <= 1) return;
    const viewportWidth = event.currentTarget.clientWidth || window.innerWidth;
    gestureRef.current = {
      pointerId: event.pointerId,
      startX: event.clientX,
      startTime: event.timeStamp,
      startPosition: activePosition,
      viewportWidth,
    };
    event.currentTarget.setPointerCapture(event.pointerId);
    setDragOffset(0);
    setIsDragging(true);
  };

  const handlePointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {
    const gesture = gestureRef.current;
    if (!gesture || gesture.pointerId !== event.pointerId) return;
    event.preventDefault();
    const distance = event.clientX - gesture.startX;
    setDragOffset(
      getResistedGalleryDragDistance(
        distance,
        gesture.startPosition > 0,
        gesture.startPosition < displayImages.length - 1,
      ),
    );
  };

  const finishPointerGesture = (
    event: ReactPointerEvent<HTMLDivElement>,
    cancelled: boolean,
  ) => {
    const gesture = gestureRef.current;
    if (!gesture || gesture.pointerId !== event.pointerId) return;
    gestureRef.current = null;
    if (event.currentTarget.hasPointerCapture(event.pointerId)) {
      event.currentTarget.releasePointerCapture(event.pointerId);
    }

    const distance = event.clientX - gesture.startX;
    const direction = cancelled
      ? 0
      : resolveGallerySwipeDirection({
          distance,
          durationMs: event.timeStamp - gesture.startTime,
          viewportWidth: gesture.viewportWidth,
          hasPrevious: gesture.startPosition > 0,
          hasNext: gesture.startPosition < displayImages.length - 1,
        });

    setIsDragging(false);
    setDragOffset(0);
    if (direction !== 0) {
      changeToPosition(gesture.startPosition + direction);
    }
  };

  return (
    <div
      className={styles.galleryOverlay}
      role="dialog"
      aria-modal="true"
      aria-label={album.title || "Private album gallery"}
    >
      <button
        type="button"
        className={styles.galleryClose}
        onClick={onClose}
        aria-label="Close private album gallery"
      >
        <X size={24} aria-hidden="true" />
      </button>

      <div
        className={styles.galleryViewport}
        data-testid="private-album-gallery-viewport"
        data-dragging={isDragging ? "true" : "false"}
        onPointerDown={handlePointerDown}
        onPointerMove={handlePointerMove}
        onPointerUp={(event) => finishPointerGesture(event, false)}
        onPointerCancel={(event) => finishPointerGesture(event, true)}
        onLostPointerCapture={(event) => finishPointerGesture(event, true)}
      >
        {displayImages.map((image, position) => {
          if (Math.abs(position - activePosition) > 1) return null;
          const slideStyle = {
            "--gallery-slide-offset": `${(position - activePosition) * 100}%`,
            "--gallery-drag-offset": `${dragOffset}px`,
          } as GallerySlideStyle;

          return (
            <div
              key={`${image.sourceIndex}:${image.url}`}
              className={styles.gallerySlide}
              style={slideStyle}
              aria-hidden={position !== activePosition}
            >
              <Image
                src={image.url}
                alt={`${album.title || "Private album"} photo ${position + 1}`}
                fill
                loading="eager"
                fetchPriority={position === activePosition ? "high" : "auto"}
                draggable={false}
                sizes="(max-width: 540px) 100vw, 540px"
                className={styles.galleryImage}
              />
            </div>
          );
        })}
      </div>

      <span className={styles.galleryCounter} aria-live="polite">
        {activePosition + 1} / {displayImages.length}
      </span>

      {hasPrevious ? (
        <button
          type="button"
          className={`${styles.galleryNav} ${styles.galleryPrevious}`}
          onClick={() => changeToPosition(activePosition - 1)}
          aria-label="Previous photo"
        >
          <ChevronLeft size={30} aria-hidden="true" />
        </button>
      ) : null}
      {hasNext ? (
        <button
          type="button"
          className={`${styles.galleryNav} ${styles.galleryNext}`}
          onClick={() => changeToPosition(activePosition + 1)}
          aria-label="Next photo"
        >
          <ChevronRight size={30} aria-hidden="true" />
        </button>
      ) : null}
    </div>
  );
}
