/**
 * ImageDetailPanel Component
 *
 * Displays enlarged image or 360/iframe embed.
 * Positioned absolutely within the canvas (not fullscreen).
 * Supports embed URL validation for security.
 */

import React, {
  useState,
  useEffect,
  useCallback,
  useRef,
  useMemo,
} from 'react';
import type { CanvasElement, InfoPanelImage } from '../../types/constructor';
import { resolveAssetPlaybackUrl } from '../../lib/assetUrl';
import { getFontByKey, getFontStyle } from '../../lib/fonts';

/**
 * Allowed embed domains for security
 */
const ALLOWED_EMBED_DOMAINS = [
  'matterport.com',
  'my.matterport.com',
  'kuula.co',
  'roundme.com',
  'sketchfab.com',
  'youtube.com',
  'www.youtube.com',
  'vimeo.com',
  'player.vimeo.com',
  'google.com',
  'maps.google.com',
  'www.google.com',
  'docs.google.com',
  'drive.google.com',
  '360stories.com',
];

/**
 * Validate if the URL is from an allowed embed domain
 */
const isValidEmbedUrl = (url: string): boolean => {
  try {
    const parsed = new URL(url);
    return ALLOWED_EMBED_DOMAINS.some(
      (domain) =>
        parsed.hostname === domain || parsed.hostname.endsWith('.' + domain),
    );
  } catch {
    return false;
  }
};

interface ImageDetailPanelProps {
  element: CanvasElement;
  image: InfoPanelImage | null;
  onClose: () => void;
  resolveUrl?: (url: string | undefined) => string;
  letterboxStyles?: React.CSSProperties;
  isEditMode?: boolean;
  onDetailPositionChange?: (xPercent: number, yPercent: number) => void;
}

const ImageDetailPanel: React.FC<ImageDetailPanelProps> = ({
  element,
  image,
  onClose,
  resolveUrl,
  letterboxStyles,
  isEditMode = false,
  onDetailPositionChange,
}) => {
  const resolve = resolveUrl ?? resolveAssetPlaybackUrl;
  const panelRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [isVisible, setIsVisible] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [embedError, setEmbedError] = useState(false);
  const [isFullscreen, setIsFullscreen] = useState(false);

  // Drag state for edit mode
  const [isDragging, setIsDragging] = useState(false);
  const dragStartRef = useRef<{
    x: number;
    y: number;
    panelX: number;
    panelY: number;
  } | null>(null);

  // Fade in animation
  useEffect(() => {
    requestAnimationFrame(() => setIsVisible(true));
  }, []);

  // Keyboard navigation (ESC to close) - only when not in fullscreen
  // (fullscreen mode handles ESC itself to exit fullscreen first)
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape' && !document.fullscreenElement) {
        e.stopPropagation();
        onClose();
      }
    };

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

  // Focus the panel on mount
  useEffect(() => {
    const panel = panelRef.current;
    if (!panel) return;
    panel.focus();
  }, []);

  // Handle fullscreen change events (user may exit via ESC or browser controls)
  useEffect(() => {
    const handleFullscreenChange = () => {
      setIsFullscreen(!!document.fullscreenElement);
    };

    document.addEventListener('fullscreenchange', handleFullscreenChange);
    document.addEventListener('webkitfullscreenchange', handleFullscreenChange);

    return () => {
      document.removeEventListener('fullscreenchange', handleFullscreenChange);
      document.removeEventListener('webkitfullscreenchange', handleFullscreenChange);
    };
  }, []);

  // Toggle fullscreen mode
  const toggleFullscreen = useCallback(async () => {
    const panel = panelRef.current;
    if (!panel) return;

    try {
      if (!document.fullscreenElement) {
        // Enter fullscreen
        if (panel.requestFullscreen) {
          await panel.requestFullscreen();
        } else if ((panel as HTMLDivElement & { webkitRequestFullscreen?: () => Promise<void> }).webkitRequestFullscreen) {
          // Safari fallback
          await (panel as HTMLDivElement & { webkitRequestFullscreen: () => Promise<void> }).webkitRequestFullscreen();
        }
      } else {
        // Exit fullscreen
        if (document.exitFullscreen) {
          await document.exitFullscreen();
        } else if ((document as Document & { webkitExitFullscreen?: () => Promise<void> }).webkitExitFullscreen) {
          // Safari fallback
          await (document as Document & { webkitExitFullscreen: () => Promise<void> }).webkitExitFullscreen();
        }
      }
    } catch {
      // Fullscreen may be blocked or not supported (iOS Safari) - silently ignore
    }
  }, []);

  // Handle backdrop click (disabled in edit mode)
  const handleBackdropClick = useCallback(
    (e: React.MouseEvent) => {
      if (e.target === e.currentTarget && !isEditMode) {
        // Exit fullscreen first if active, then close
        if (document.fullscreenElement) {
          // eslint-disable-next-line @typescript-eslint/no-empty-function
          document.exitFullscreen().catch(() => {});
        }
        onClose();
      }
    },
    [onClose, isEditMode],
  );

  // Handle close with fullscreen exit
  const handleClose = useCallback(async () => {
    // Exit fullscreen before closing if in fullscreen mode
    if (document.fullscreenElement) {
      try {
        await document.exitFullscreen();
      } catch {
        // Ignore errors
      }
    }
    onClose();
  }, [onClose]);

  // Extract detail panel styling from element
  const detailXPercent = element.detailXPercent ?? 75;
  const detailYPercent = element.detailYPercent ?? 50;
  const detailWidth = element.detailWidth ?? '500';
  const detailHeight = element.detailHeight ?? '400';
  const detailBackgroundColor =
    element.detailBackgroundColor ?? 'rgba(0, 0, 0, 0.9)';
  const detailBorderRadius = element.detailBorderRadius ?? '12';
  const detailBorderWidth = element.detailBorderWidth ?? '0';
  const detailBorderColor = element.detailBorderColor ?? 'transparent';
  const detailBorderStyle = element.detailBorderStyle ?? 'solid';
  const detailPadding = element.detailPadding ?? '12';

  // Caption font style
  const captionFontStyle = useMemo(() => {
    const fontKey = element.detailCaptionFontFamily;
    if (!fontKey) return {};
    const font = getFontByKey(fontKey);
    return font ? getFontStyle(font) : {};
  }, [element.detailCaptionFontFamily]);

  // Note: ImageDetailPanel doesn't render its own overlay backdrop.
  // The parent InfoPanelOverlay already provides the backdrop when the info panel is open.

  // Determine content type (handle null image for edit mode placeholder)
  const isEmbed = image?.itemType === '360' && image?.embedUrl;
  const embedUrl = image?.embedUrl ?? '';
  const isValidEmbed = isEmbed && isValidEmbedUrl(embedUrl);
  const hasImage = !!image;

  // Convert numeric values to canvas units for responsive scaling
  const toCU = (value: string): string => {
    const trimmed = value.trim();
    // Zero doesn't need a unit
    if (trimmed === '0') return '0';
    // Already uses canvas units - return as-is
    if (trimmed.includes('var(--cu') || trimmed.includes('--cu')) return trimmed;
    // CSS functions (calc, var, min, max, etc.) - return as-is
    if (/^(calc|var|min|max|clamp)\(/i.test(trimmed)) return trimmed;
    // Already has a unit suffix - return as-is
    if (/[a-z%]+$/i.test(trimmed)) return trimmed;
    // Parse numeric value and convert to canvas units
    const num = parseFloat(trimmed);
    if (!Number.isFinite(num)) return trimmed;
    return `calc(${num} * var(--cu, 1px))`;
  };

  // Panel style - fullscreen mode overrides positioning and sizing
  const panelStyle: React.CSSProperties = isFullscreen
    ? {
        position: 'fixed',
        inset: 0,
        width: '100%',
        height: '100%',
        backgroundColor: '#000',
        borderRadius: 0,
        padding: 0,
        overflow: 'hidden',
        opacity: 1,
        border: 'none',
        zIndex: 9999,
      }
    : {
        position: 'absolute',
        left: `${detailXPercent}%`,
        top: `${detailYPercent}%`,
        transform: 'translate(-50%, -50%)',
        width: `min(${toCU(detailWidth)}, calc(100vw - 32px))`,
        height: `min(${toCU(detailHeight)}, calc(100dvh - 64px))`,
        backgroundColor: detailBackgroundColor,
        borderRadius: toCU(detailBorderRadius),
        padding: isEmbed ? 0 : toCU(detailPadding),
        overflow: 'hidden',
        opacity: isVisible ? 1 : 0,
        transition: 'opacity 200ms ease-out',
        border:
          detailBorderWidth !== '0' && detailBorderWidth
            ? `${toCU(detailBorderWidth)} ${detailBorderStyle} ${detailBorderColor}`
            : 'none',
      };

  // Handle iframe load
  const handleIframeLoad = () => {
    setIsLoading(false);
  };

  // Handle iframe error
  const handleIframeError = () => {
    setIsLoading(false);
    setEmbedError(true);
  };

  // Handle image load
  const handleImageLoad = () => {
    setIsLoading(false);
  };

  // Drag handlers for edit mode
  const handleDragStart = useCallback(
    (e: React.MouseEvent) => {
      if (!isEditMode || !onDetailPositionChange) return;

      e.preventDefault();
      e.stopPropagation();
      setIsDragging(true);

      dragStartRef.current = {
        x: e.clientX,
        y: e.clientY,
        panelX: element.detailXPercent ?? 75,
        panelY: element.detailYPercent ?? 50,
      };
    },
    [
      isEditMode,
      onDetailPositionChange,
      element.detailXPercent,
      element.detailYPercent,
    ],
  );

  useEffect(() => {
    if (!isDragging || !containerRef.current) return;

    const container = containerRef.current;

    const handleMouseMove = (e: MouseEvent) => {
      if (!dragStartRef.current) return;

      const rect = container.getBoundingClientRect();
      const deltaX = e.clientX - dragStartRef.current.x;
      const deltaY = e.clientY - dragStartRef.current.y;

      // Convert pixel delta to percentage
      const deltaXPercent = (deltaX / rect.width) * 100;
      const deltaYPercent = (deltaY / rect.height) * 100;

      const newX = Math.max(
        0,
        Math.min(100, dragStartRef.current.panelX + deltaXPercent),
      );
      const newY = Math.max(
        0,
        Math.min(100, dragStartRef.current.panelY + deltaYPercent),
      );

      onDetailPositionChange?.(Math.round(newX), Math.round(newY));
    };

    const handleMouseUp = () => {
      setIsDragging(false);
      dragStartRef.current = null;
    };

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);

    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isDragging, onDetailPositionChange]);

  return (
    <>
      {/* Click backdrop (transparent) - InfoPanelOverlay provides the visual backdrop */}
      <div
        className='fixed inset-0 z-[52] overflow-hidden'
        style={{
          backgroundColor: 'transparent',
          pointerEvents: isEditMode ? 'none' : 'auto',
        }}
        onClick={handleBackdropClick}
      />

      {/* Inner container constrained to canvas bounds */}
      <div
        ref={containerRef}
        className='fixed inset-0 z-[54] overflow-hidden pointer-events-none'
        style={letterboxStyles || { position: 'absolute', inset: 0 }}
      >
        {/* Detail Panel */}
        <div
          ref={panelRef}
          role='dialog'
          aria-modal='true'
          aria-label={image?.caption || 'Image detail'}
          tabIndex={-1}
          style={{
            ...panelStyle,
            pointerEvents: 'auto', // Ensure panel receives events
            cursor:
              isEditMode && onDetailPositionChange
                ? isDragging
                  ? 'grabbing'
                  : 'grab'
                : undefined,
          }}
          onClick={(e) => e.stopPropagation()}
          onMouseDown={
            isEditMode && onDetailPositionChange ? handleDragStart : undefined
          }
        >
          {/* Control buttons - only show for regular images (embeds have their own controls) */}
          {!isEmbed && image?.imageUrl && (
            <>
              {/* Close button */}
              <button
                type='button'
                className='absolute top-2 right-2 z-10 p-1 rounded-full text-white/70 hover:text-white hover:bg-white/10 transition-colors'
                onClick={handleClose}
                aria-label='Close detail view'
              >
                <svg
                  xmlns='http://www.w3.org/2000/svg'
                  fill='none'
                  viewBox='0 0 24 24'
                  strokeWidth={2}
                  stroke='currentColor'
                  className='w-5 h-5'
                >
                  <path
                    strokeLinecap='round'
                    strokeLinejoin='round'
                    d='M6 18 18 6M6 6l12 12'
                  />
                </svg>
              </button>

              {/* Fullscreen toggle button */}
              <button
                type='button'
                className='absolute top-2 right-10 z-10 p-1 rounded-full text-white/70 hover:text-white hover:bg-white/10 transition-colors'
                onClick={toggleFullscreen}
                aria-label={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
              >
                {isFullscreen ? (
                  <svg
                    xmlns='http://www.w3.org/2000/svg'
                    fill='none'
                    viewBox='0 0 24 24'
                    strokeWidth={2}
                    stroke='currentColor'
                    className='w-5 h-5'
                  >
                    <path
                      strokeLinecap='round'
                      strokeLinejoin='round'
                      d='M9 9V4.5M9 9H4.5M9 9 3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25'
                    />
                  </svg>
                ) : (
                  <svg
                    xmlns='http://www.w3.org/2000/svg'
                    fill='none'
                    viewBox='0 0 24 24'
                    strokeWidth={2}
                    stroke='currentColor'
                    className='w-5 h-5'
                  >
                    <path
                      strokeLinecap='round'
                      strokeLinejoin='round'
                      d='M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15'
                    />
                  </svg>
                )}
              </button>
            </>
          )}

          {/* Loading spinner */}
          {isLoading && (
            <div className='absolute inset-0 flex items-center justify-center'>
              <div className='w-10 h-10 border-4 border-white/20 border-t-white rounded-full animate-spin' />
            </div>
          )}

          {/* Content area - disable pointer events in edit mode so panel can be dragged */}
          <div
            className='w-full h-full'
            style={{ pointerEvents: isEditMode ? 'none' : 'auto' }}
          >
            {!hasImage ? (
              // Edit mode placeholder when no image selected
              <div className='w-full h-full flex flex-col items-center justify-center text-white/50'>
                <svg
                  xmlns='http://www.w3.org/2000/svg'
                  fill='none'
                  viewBox='0 0 24 24'
                  strokeWidth={1.5}
                  stroke='currentColor'
                  className='w-16 h-16 mb-3'
                >
                  <path
                    strokeLinecap='round'
                    strokeLinejoin='round'
                    d='m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z'
                  />
                </svg>
                <p className='text-sm font-medium'>Image Detail Panel</p>
                <p className='text-xs mt-1 text-white/30'>
                  Click an image in Info Panel to preview
                </p>
              </div>
            ) : isEmbed ? (
              // Embed iframe
              isValidEmbed ? (
                <iframe
                  src={embedUrl}
                  title={image?.caption || 'Embedded content'}
                  className='w-full h-full border-0'
                  sandbox='allow-scripts allow-same-origin allow-presentation allow-popups'
                  allow='accelerometer; autoplay; fullscreen; gyroscope; xr-spatial-tracking'
                  loading='lazy'
                  onLoad={handleIframeLoad}
                  onError={handleIframeError}
                  style={{
                    opacity: isLoading ? 0 : 1,
                    WebkitOverflowScrolling: 'touch',
                  }}
                />
              ) : (
                // Invalid embed URL error
                <div className='w-full h-full flex flex-col items-center justify-center text-white/60'>
                  <svg
                    xmlns='http://www.w3.org/2000/svg'
                    fill='none'
                    viewBox='0 0 24 24'
                    strokeWidth={1.5}
                    stroke='currentColor'
                    className='w-12 h-12 mb-2 text-red-400'
                  >
                    <path
                      strokeLinecap='round'
                      strokeLinejoin='round'
                      d='M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z'
                    />
                  </svg>
                  <p className='text-sm'>Invalid or unsupported embed URL</p>
                  <p className='text-xs mt-1 text-white/40'>
                    Only trusted domains are allowed
                  </p>
                </div>
              )
            ) : image?.imageUrl ? (
              // Regular image
              // eslint-disable-next-line @next/next/no-img-element
              <img
                src={resolve(image.imageUrl)}
                alt={image?.caption || ''}
                className='w-full h-full object-contain'
                draggable={false}
                onLoad={handleImageLoad}
                style={{
                  opacity: isLoading ? 0 : 1,
                  transition: 'opacity 200ms ease-out',
                }}
              />
            ) : (
              // No content placeholder
              <div className='w-full h-full flex items-center justify-center text-white/40'>
                <p>No content</p>
              </div>
            )}

            {/* Embed error state */}
            {embedError && (
              <div className='absolute inset-0 flex flex-col items-center justify-center text-white/60 bg-black/50'>
                <svg
                  xmlns='http://www.w3.org/2000/svg'
                  fill='none'
                  viewBox='0 0 24 24'
                  strokeWidth={1.5}
                  stroke='currentColor'
                  className='w-12 h-12 mb-2 text-red-400'
                >
                  <path
                    strokeLinecap='round'
                    strokeLinejoin='round'
                    d='M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z'
                  />
                </svg>
                <p className='text-sm'>Failed to load embed</p>
              </div>
            )}
          </div>

          {/* Caption */}
          {image?.caption && (
            <div className='absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-3'>
              <p className='text-sm text-white' style={captionFontStyle}>
                {image.caption}
              </p>
            </div>
          )}
        </div>
      </div>
    </>
  );
};

export default ImageDetailPanel;
