/**
 * GalleryCarouselOverlay Component
 *
 * Fullscreen carousel overlay for gallery elements.
 * Shows images in a slideshow with navigation buttons.
 * In constructor mode, buttons are draggable for positioning.
 */

import React, { useState, useEffect, useCallback, useRef } from 'react';
import Icon from '@mdi/react';
import { mdiChevronLeft, mdiChevronRight, mdiArrowLeft } from '@mdi/js';
import type {
  GalleryCard,
  GalleryCarouselMediaItem,
  CanvasElement,
} from '../../types/constructor';
import type { ResolvedTransitionSettings } from '../../types/transition';
import { resolveAssetPlaybackUrl } from '../../lib/assetUrl';
import {
  resolveSlideTransition,
  extractGallerySlideOverride,
} from '../../lib/resolveSlideTransition';
import { useSlideTransition } from '../../hooks/useSlideTransition';
import { useGlobalAudioMute } from '../../hooks/useGlobalAudioMute';
import { buildChromeFreeEmbedUrl, isValidEmbedUrl } from '../../lib/embedUrl';

interface GalleryCarouselOverlayProps {
  cards: Array<GalleryCard | GalleryCarouselMediaItem>;
  initialIndex: number;
  onClose: () => void;
  resolveUrl?: (url: string | undefined) => string;
  // Button icons (MDI fallback if not set)
  prevIconUrl?: string;
  nextIconUrl?: string;
  backIconUrl?: string;
  backLabel?: string;
  // Button positions (percentage-based, like canvas elements)
  prevX?: number;
  prevY?: number;
  nextX?: number;
  nextY?: number;
  backX?: number;
  backY?: number;
  // Button dimensions (numeric values use vw for width, vh for height - same as regular elements)
  // When set with custom icon, button renders like NavigationElement (icon fills full button)
  prevWidth?: string;
  prevHeight?: string;
  nextWidth?: string;
  nextHeight?: string;
  backWidth?: string;
  backHeight?: string;
  // Constructor mode: buttons draggable
  isEditMode?: boolean;
  onButtonPositionChange?: (
    button: 'prev' | 'next' | 'back',
    x: number,
    y: number,
  ) => void;
  // Letterbox styles for constraining overlay to canvas bounds
  letterboxStyles?: React.CSSProperties;
  // Page transition settings (for slide transition cascade)
  pageTransitionSettings?: ResolvedTransitionSettings;
  // Gallery element (for extracting slide transition override)
  galleryElement?: CanvasElement;
}

const getMediaType = (
  card: GalleryCard | GalleryCarouselMediaItem | undefined,
): 'image' | 'video' | '360' => {
  if (!card) return 'image';
  if ('mediaType' in card && card.mediaType) return card.mediaType;
  if ('videoUrl' in card && card.videoUrl) return 'video';
  if ('embedUrl' in card && card.embedUrl) return '360';
  return 'image';
};

const getMediaTitle = (
  card: GalleryCard | GalleryCarouselMediaItem | undefined,
): string => {
  if (!card) return '';
  return (
    ('title' in card && card.title) ||
    ('caption' in card && card.caption) ||
    ('description' in card && card.description) ||
    ''
  );
};

const getVideoUrl = (
  card: GalleryCard | GalleryCarouselMediaItem | undefined,
): string => (card && 'videoUrl' in card && card.videoUrl ? card.videoUrl : '');

const getEmbedUrl = (
  card: GalleryCard | GalleryCarouselMediaItem | undefined,
): string => (card && 'embedUrl' in card && card.embedUrl ? card.embedUrl : '');

const clamp = (value: number, min: number, max: number) =>
  Math.min(Math.max(value, min), max);

const GalleryCarouselOverlay: React.FC<GalleryCarouselOverlayProps> = ({
  cards,
  initialIndex,
  onClose,
  resolveUrl,
  prevIconUrl,
  nextIconUrl,
  backIconUrl,
  backLabel = 'BACK',
  prevX = 5,
  prevY = 50,
  nextX = 95,
  nextY = 50,
  backX = 5,
  backY = 90,
  prevWidth,
  prevHeight,
  nextWidth,
  nextHeight,
  backWidth,
  backHeight,
  isEditMode = false,
  onButtonPositionChange,
  letterboxStyles,
  pageTransitionSettings,
  galleryElement,
}) => {
  const resolve = resolveUrl ?? resolveAssetPlaybackUrl;
  const { isMuted } = useGlobalAudioMute();

  // Resolve slide transition with cascade
  const slideTransition = resolveSlideTransition(
    pageTransitionSettings,
    extractGallerySlideOverride(galleryElement),
  );

  // Use hook for animation state
  const {
    displayIndex,
    overlayOpacity,
    overlayColor,
    goToIndex,
    setInitialIndex,
    slideTransitionStyle,
    overlayTransitionStyle,
    slideOpacity,
  } = useSlideTransition(slideTransition);

  // Set initial index on mount
  useEffect(() => {
    setInitialIndex(initialIndex);
  }, [initialIndex, setInitialIndex]);
  const [draggingButton, setDraggingButton] = useState<
    'prev' | 'next' | 'back' | null
  >(null);
  // Clamp positions to canvas bounds (0-100%)
  const [positions, setPositions] = useState({
    prevX: clamp(prevX, 0, 100),
    prevY: clamp(prevY, 0, 100),
    nextX: clamp(nextX, 0, 100),
    nextY: clamp(nextY, 0, 100),
    backX: clamp(backX, 0, 100),
    backY: clamp(backY, 0, 100),
  });
  const overlayRef = useRef<HTMLDivElement>(null);
  const canvasContainerRef = useRef<HTMLDivElement>(null);
  const touchStartRef = useRef<{ x: number; y: number } | null>(null);
  const positionsRef = useRef(positions);
  positionsRef.current = positions;

  // Update positions when props change (clamped to canvas bounds)
  useEffect(() => {
    setPositions({
      prevX: clamp(prevX, 0, 100),
      prevY: clamp(prevY, 0, 100),
      nextX: clamp(nextX, 0, 100),
      nextY: clamp(nextY, 0, 100),
      backX: clamp(backX, 0, 100),
      backY: clamp(backY, 0, 100),
    });
  }, [prevX, prevY, nextX, nextY, backX, backY]);

  // Navigation handlers
  const goToPrev = useCallback(() => {
    if (cards.length === 0) return;
    const newIndex = (displayIndex - 1 + cards.length) % cards.length;
    goToIndex(newIndex);
  }, [cards.length, displayIndex, goToIndex]);

  const goToNext = useCallback(() => {
    if (cards.length === 0) return;
    const newIndex = (displayIndex + 1) % cards.length;
    goToIndex(newIndex);
  }, [cards.length, displayIndex, goToIndex]);

  // Keyboard navigation
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        onClose();
      } else if (e.key === 'ArrowLeft') {
        goToPrev();
      } else if (e.key === 'ArrowRight') {
        goToNext();
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [onClose, goToPrev, goToNext]);

  // Touch swipe handling
  const handleTouchStart = (e: React.TouchEvent) => {
    if (isEditMode) return;
    touchStartRef.current = {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY,
    };
  };

  const handleTouchEnd = (e: React.TouchEvent) => {
    if (isEditMode || !touchStartRef.current) return;

    const touchEnd = {
      x: e.changedTouches[0].clientX,
      y: e.changedTouches[0].clientY,
    };

    const deltaX = touchEnd.x - touchStartRef.current.x;
    const threshold = 50;

    if (Math.abs(deltaX) > threshold) {
      if (deltaX > 0) {
        goToPrev();
      } else {
        goToNext();
      }
    }

    touchStartRef.current = null;
  };

  // Draggable button handling (constructor mode only)
  const handleButtonDragStart = (
    button: 'prev' | 'next' | 'back',
    e: React.MouseEvent,
  ) => {
    if (!isEditMode) return;
    e.preventDefault();
    e.stopPropagation();
    setDraggingButton(button);
  };

  useEffect(() => {
    if (!isEditMode || !draggingButton) return;

    const handleMove = (e: MouseEvent) => {
      // Calculate position relative to canvas container (strict - no viewport fallback)
      const container = canvasContainerRef.current;
      if (!container) return;

      const rect = container.getBoundingClientRect();
      const x = ((e.clientX - rect.left) / rect.width) * 100;
      const y = ((e.clientY - rect.top) / rect.height) * 100;
      // Clamp to canvas bounds (0-100%)
      const clampedX = clamp(x, 0, 100);
      const clampedY = clamp(y, 0, 100);

      setPositions((prev) => {
        // Prev and next buttons share the same Y coordinate
        if (draggingButton === 'prev' || draggingButton === 'next') {
          return {
            ...prev,
            [`${draggingButton}X`]: clampedX,
            prevY: clampedY,
            nextY: clampedY,
          };
        }
        // Back button has independent position
        return {
          ...prev,
          [`${draggingButton}X`]: clampedX,
          [`${draggingButton}Y`]: clampedY,
        };
      });
    };

    const handleUp = () => {
      if (onButtonPositionChange && draggingButton) {
        const currentPositions = positionsRef.current;
        const posKey = `${draggingButton}X` as keyof typeof positions;
        const posKeyY = `${draggingButton}Y` as keyof typeof positions;
        onButtonPositionChange(
          draggingButton,
          currentPositions[posKey],
          currentPositions[posKeyY],
        );
        // For prev/next, also update the other button's Y position
        if (draggingButton === 'prev') {
          onButtonPositionChange(
            'next',
            currentPositions.nextX,
            currentPositions.prevY,
          );
        } else if (draggingButton === 'next') {
          onButtonPositionChange(
            'prev',
            currentPositions.prevX,
            currentPositions.nextY,
          );
        }
      }
      setDraggingButton(null);
    };

    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', handleUp);
    return () => {
      window.removeEventListener('mousemove', handleMove);
      window.removeEventListener('mouseup', handleUp);
    };
  }, [isEditMode, draggingButton, onButtonPositionChange]);

  // Convert numeric value to viewport units (vw for width, vh for height)
  // Matches the unit system used by regular elements in elementStyles.ts
  const toViewportUnit = (
    value?: string,
    unit: 'vw' | 'vh' = 'vw',
  ): string | undefined => {
    if (!value || value.trim() === '') return undefined;
    const trimmed = value.trim();
    // If value already has a unit, preserve it
    if (/[a-z%]+$/i.test(trimmed)) return trimmed;
    const num = parseFloat(trimmed);
    if (!Number.isFinite(num) || num <= 0) return undefined;
    return `${num}${unit}`;
  };

  // Render navigation button
  // When custom icon is set, render like NavigationElement (icon fills full button, no backdrop)
  // Otherwise, use MDI fallback with backdrop styling
  const renderNavButton = (
    type: 'prev' | 'next' | 'back',
    x: number,
    y: number,
    iconUrl?: string,
    defaultIcon?: string,
    label?: string,
    buttonWidth?: string,
    buttonHeight?: string,
  ) => {
    const isDragging = draggingButton === type;
    const isNavButton = type === 'prev' || type === 'next';
    const hasCustomIcon = iconUrl && iconUrl.trim() !== '';
    const widthValue = toViewportUnit(buttonWidth, 'vw');
    const heightValue = toViewportUnit(buttonHeight, 'vh');

    // Navigation-style rendering: custom icon fills full button (like NavigationElement)
    // When custom icon is set, always use navigation style (icon only, no backdrop)
    const useNavigationStyle = hasCustomIcon;

    return (
      <button
        type='button'
        className={`absolute flex items-center justify-center transition-transform ${
          isEditMode
            ? 'cursor-move hover:scale-110'
            : 'cursor-pointer hover:scale-105'
        } ${isDragging ? 'scale-110 z-[60]' : ''}`}
        style={{
          left: `${x}%`,
          top: `${y}%`,
          transform: 'translate(-50%, -50%)',
          // Apply dimensions when set (vw for width, vh for height - matches regular elements)
          ...(widthValue && { width: widthValue }),
          ...(heightValue && { height: heightValue }),
        }}
        onClick={(e) => {
          e.stopPropagation();
          if (isEditMode) return;
          if (type === 'prev') goToPrev();
          else if (type === 'next') goToNext();
          else onClose();
        }}
        onMouseDown={(e) => handleButtonDragStart(type, e)}
      >
        {useNavigationStyle ? (
          // Navigation-style: icon fills full button, no backdrop, no label
          // eslint-disable-next-line @next/next/no-img-element
          <img
            src={resolve(iconUrl)}
            alt=''
            style={{
              width: '100%',
              height: '100%',
              objectFit: 'contain',
            }}
            draggable={false}
          />
        ) : (
          // Default style: MDI icon with backdrop
          <div
            className={`flex items-center gap-2 ${
              isNavButton
                ? 'rounded-full bg-black/40 p-3 backdrop-blur-sm'
                : 'rounded-lg bg-black/40 px-4 py-2 backdrop-blur-sm'
            }`}
          >
            {defaultIcon && (
              <Icon
                path={defaultIcon}
                size={isNavButton ? 1.5 : 1}
                className='text-white'
              />
            )}
            {label && (
              <span className='text-sm font-medium text-white'>{label}</span>
            )}
          </div>
        )}
      </button>
    );
  };

  const currentCard = cards[displayIndex];
  const mediaType = getMediaType(currentCard);
  const imageUrl = currentCard?.imageUrl ? resolve(currentCard.imageUrl) : '';
  const rawVideoUrl = getVideoUrl(currentCard);
  const videoUrl = rawVideoUrl ? resolve(rawVideoUrl) : '';
  const embedUrl = getEmbedUrl(currentCard);
  const embedSrc =
    embedUrl && isValidEmbedUrl(embedUrl)
      ? buildChromeFreeEmbedUrl(embedUrl)
      : '';
  const mediaTitle = getMediaTitle(currentCard);

  return (
    <div
      ref={overlayRef}
      className='fixed inset-0 z-[120] overflow-hidden bg-black'
      onClick={(e) => {
        // Only close if clicking the background, not buttons
        if (e.target === overlayRef.current && !isEditMode) {
          onClose();
        }
      }}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
    >
      {/* Inner container constrained to canvas bounds - no conflicting CSS classes */}
      <div
        ref={canvasContainerRef}
        className='overflow-hidden'
        style={letterboxStyles || { position: 'absolute', inset: 0 }}
      >
        {/* Fullscreen media */}
        {mediaType === 'image' && imageUrl && (
          // eslint-disable-next-line @next/next/no-img-element
          <img
            src={imageUrl}
            alt={mediaTitle}
            className='absolute inset-0 h-full w-full object-contain'
            style={{ ...slideTransitionStyle, opacity: slideOpacity }}
            draggable={false}
          />
        )}
        {mediaType === 'video' && videoUrl && (
          <video
            key={videoUrl}
            src={videoUrl}
            className='absolute inset-0 h-full w-full object-contain'
            style={{ ...slideTransitionStyle, opacity: slideOpacity }}
            controls
            autoPlay
            muted={isMuted}
            playsInline
          />
        )}
        {mediaType === '360' && embedUrl && isValidEmbedUrl(embedUrl) && (
          <iframe
            key={embedSrc}
            src={embedSrc}
            title={mediaTitle || '360 view'}
            className='absolute inset-0 h-full w-full border-0'
            style={{ ...slideTransitionStyle, opacity: slideOpacity }}
            allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; xr-spatial-tracking'
          />
        )}
        {mediaType === '360' && embedUrl && !isValidEmbedUrl(embedUrl) && (
          <div className='absolute inset-0 flex items-center justify-center text-white/70'>
            Embed domain is not allowed
          </div>
        )}
        {/* Transition overlay (fades in during fadingOut, fades out during fadingIn) */}
        {slideTransition.type === 'fade' && (
          <div
            className='absolute inset-0 pointer-events-none'
            style={{
              ...overlayTransitionStyle,
              backgroundColor: overlayColor,
              opacity: overlayOpacity,
            }}
          />
        )}

        {/* Prev button */}
        {renderNavButton(
          'prev',
          positions.prevX,
          positions.prevY,
          prevIconUrl,
          mdiChevronLeft,
          undefined,
          prevWidth,
          prevHeight,
        )}

        {/* Next button */}
        {renderNavButton(
          'next',
          positions.nextX,
          positions.nextY,
          nextIconUrl,
          mdiChevronRight,
          undefined,
          nextWidth,
          nextHeight,
        )}

        {/* Back button */}
        {renderNavButton(
          'back',
          positions.backX,
          positions.backY,
          backIconUrl,
          mdiArrowLeft,
          backLabel,
          backWidth,
          backHeight,
        )}
      </div>
    </div>
  );
};

export default GalleryCarouselOverlay;
