import React, { CSSProperties, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { ArrowLeftIcon, ArrowRightIcon } from '../ui/icons';
import { clamp } from '../utils/math';

type PreviewNavigator = {
  url: string;
  alt?: string;
  canGoPrev?: boolean;
  canGoNext?: boolean;
  goPrev?: () => void;
  goNext?: () => void;
};

interface PreviewZoomOverlayProps {
  open?: boolean;
  display?: PreviewNavigator | null;
  onClose?: () => void;
}

type NaturalSize = { width: number | null; height: number | null };
type FocusPoint = { xRatio: number; yRatio: number } | null;

const noop = () => {};

const PreviewZoomOverlay: React.FC<PreviewZoomOverlayProps> = ({
  open = false,
  display = null,
  onClose = noop,
}) => {
  const portalTarget = typeof document !== 'undefined' ? document.body : null;
  const [isNativeScale, setIsNativeScale] = useState(false);
  const [naturalSize, setNaturalSize] = useState<NaturalSize>({ width: null, height: null });
  const [renderBackdrop, setRenderBackdrop] = useState(false);
  const [isBackdropVisible, setBackdropVisible] = useState(false);
  const [displaySnapshot, setDisplaySnapshot] = useState<PreviewNavigator | null>(null);
  const scrollRef = useRef<HTMLDivElement | null>(null);
  const imageRef = useRef<HTMLImageElement | null>(null);
  const focusRef = useRef<FocusPoint>(null);
  const previouslyFocusedRef = useRef<HTMLElement | null>(null);
  const visibilityTimerRef = useRef<number | null>(null);
  const displayTimerRef = useRef<number | null>(null);

  useEffect(() => {
    if (display?.url) {
      setDisplaySnapshot(display);
    }
  }, [display]);

  useEffect(() => {
    if (visibilityTimerRef.current) {
      clearTimeout(visibilityTimerRef.current);
      visibilityTimerRef.current = null;
    }
    if (displayTimerRef.current) {
      cancelAnimationFrame(displayTimerRef.current);
      displayTimerRef.current = null;
    }

    if (open && display?.url) {
      setRenderBackdrop(true);
      displayTimerRef.current = requestAnimationFrame(() => {
        displayTimerRef.current = requestAnimationFrame(() => {
          setBackdropVisible(true);
        });
      });
      return () => {
        if (displayTimerRef.current) {
          cancelAnimationFrame(displayTimerRef.current);
          displayTimerRef.current = null;
        }
      };
    }

    setBackdropVisible(false);
    visibilityTimerRef.current = window.setTimeout(() => {
      setRenderBackdrop(false);
    }, 260);

    return () => {
      if (visibilityTimerRef.current) {
        clearTimeout(visibilityTimerRef.current);
        visibilityTimerRef.current = null;
      }
    };
  }, [open, display?.url]);

  useEffect(() => () => {
    if (visibilityTimerRef.current) {
      clearTimeout(visibilityTimerRef.current);
    }
    if (displayTimerRef.current) {
      cancelAnimationFrame(displayTimerRef.current);
    }
  }, []);

  useEffect(() => {
    setIsNativeScale(false);
    setNaturalSize({ width: null, height: null });
    focusRef.current = null;
    const scrollEl = scrollRef.current;
    if (scrollEl) {
      scrollEl.scrollLeft = 0;
      scrollEl.scrollTop = 0;
    }
  }, [open]);


  useEffect(() => {
    if (!open || !isNativeScale) {
      return;
    }

    const scrollEl = scrollRef.current;
    const imageEl = imageRef.current;
    if (!scrollEl || !imageEl) {
      return;
    }

    const imageWidth = imageEl.naturalWidth || imageEl.clientWidth;
    const imageHeight = imageEl.naturalHeight || imageEl.clientHeight;
    if (!(imageWidth > 0 && imageHeight > 0)) {
      return;
    }

    const target = focusRef.current || { xRatio: 0.5, yRatio: 0.5 };
    const maxScrollLeft = Math.max(0, imageWidth - scrollEl.clientWidth);
    const maxScrollTop = Math.max(0, imageHeight - scrollEl.clientHeight);

    const desiredLeft = target.xRatio * imageWidth - scrollEl.clientWidth / 2;
    const desiredTop = target.yRatio * imageHeight - scrollEl.clientHeight / 2;

    scrollEl.scrollLeft = clamp(desiredLeft, 0, maxScrollLeft);
    scrollEl.scrollTop = clamp(desiredTop, 0, maxScrollTop);
  }, [open, isNativeScale, naturalSize.width, naturalSize.height]);

  useEffect(() => {
    if (typeof document === 'undefined') {
      return;
    }
    if (!open) {
      previouslyFocusedRef.current?.focus?.();
      previouslyFocusedRef.current = null;
      return;
    }

    const active = document.activeElement;
    previouslyFocusedRef.current = active instanceof HTMLElement ? active : null;
  }, [open]);

  const activeDisplay = open && display?.url ? display : displaySnapshot;

  useEffect(() => {
    if (!activeDisplay?.url) {
      return;
    }

    const scrollEl = scrollRef.current;
    if (scrollEl) {
      scrollEl.scrollLeft = 0;
      scrollEl.scrollTop = 0;
    }
    focusRef.current = null;
  }, [activeDisplay?.url]);

  useEffect(() => {
    if (!renderBackdrop || !activeDisplay?.url) {
      return undefined;
    }

    const frame = requestAnimationFrame(() => {
      scrollRef.current?.focus?.({ preventScroll: true });
    });

    return () => cancelAnimationFrame(frame);
  }, [renderBackdrop, activeDisplay?.url]);

  const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
    event.stopPropagation();

    if (!open) {
      return;
    }

    const key = event.key;

    if (key === ' ' || key === 'Space' || key === 'Spacebar') {
      const target = event.target;
      if (target instanceof HTMLElement) {
        const tag = target.tagName ? target.tagName.toLowerCase() : '';
        if (
          target.isContentEditable
          || tag === 'input'
          || tag === 'textarea'
          || tag === 'select'
        ) {
          return;
        }
      }
      event.preventDefault();
      onClose();
      return;
    }

    if (key === 'Escape') {
      event.preventDefault();
      onClose();
      return;
    }

    if (key === 'ArrowLeft') {
      if (activeDisplay?.canGoPrev && activeDisplay?.goPrev) {
        event.preventDefault();
        activeDisplay.goPrev();
      }
      return;
    }

    if (key === 'ArrowRight') {
      if (activeDisplay?.canGoNext && activeDisplay?.goNext) {
        event.preventDefault();
        activeDisplay.goNext();
      }
    }
  };

  const toggleZoomAtPoint = (clientX: number, clientY: number) => {
    const img = imageRef.current;
    setIsNativeScale((current) => {
      if (!current && img) {
        const rect = img.getBoundingClientRect();
        const xRatio = rect.width > 0 ? (clientX - rect.left) / rect.width : 0.5;
        const yRatio = rect.height > 0 ? (clientY - rect.top) / rect.height : 0.5;
        focusRef.current = {
          xRatio: clamp(xRatio, 0, 1),
          yRatio: clamp(yRatio, 0, 1),
        };
      } else {
        focusRef.current = null;
      }
      return !current;
    });
  };

  const handleImageClick = (event: React.MouseEvent<HTMLImageElement>) => {
    event.stopPropagation();
    toggleZoomAtPoint(event.clientX ?? 0, event.clientY ?? 0);
  };

  if (!renderBackdrop || !activeDisplay?.url || !portalTarget) {
    return null;
  }

  const effectiveDisplay = activeDisplay;
  const navVisible = Boolean(effectiveDisplay?.canGoPrev || effectiveDisplay?.canGoNext);
  const stageClassName = 'preview-zoom__stage';

  const containerClassName = [
    'preview-zoom__scroll',
    isNativeScale ? 'preview-zoom__scroll--native' : '',
  ]
    .filter(Boolean)
    .join(' ');

  const backdropClassName = [
    'preview-zoom-backdrop',
    isBackdropVisible ? 'preview-zoom-backdrop--visible' : '',
  ]
    .filter(Boolean)
    .join(' ');

  const imageStyle: CSSProperties = isNativeScale
    ? {
        cursor: 'zoom-out',
        width: naturalSize.width ? `${naturalSize.width}px` : 'auto',
        height: naturalSize.height ? `${naturalSize.height}px` : 'auto',
        maxWidth: 'none',
        maxHeight: 'none',
        touchAction: 'manipulation',
      }
    : {
        cursor: 'zoom-in',
        maxWidth: '95vw',
        maxHeight: '95vh',
        touchAction: 'manipulation',
      };

  return createPortal(
    (
      <div
        className={backdropClassName}
        role="dialog"
        aria-modal="true"
        aria-label="Enlarged document preview"
        onClick={onClose}
      >
        <div className={stageClassName} onKeyDown={handleKeyDown}>
          <div
            className={containerClassName}
            ref={scrollRef}
            tabIndex={-1}
            onClick={(event) => event.stopPropagation()}
          >
            <img
              src={effectiveDisplay.url}
              alt={effectiveDisplay.alt || 'Document preview'}
              className="preview-zoom__image"
              ref={imageRef}
              draggable={false}
              onLoad={(event) => {
                setNaturalSize({
                  width: event.currentTarget.naturalWidth || null,
                  height: event.currentTarget.naturalHeight || null,
                });
              }}
              onClick={handleImageClick}
              style={imageStyle}
            />
          </div>
          {navVisible ? (
            <div className="preview-zoom__nav">
              <button
                type="button"
                className="preview-zoom__nav-button"
                onClick={(event) => {
                  event.stopPropagation();
                  if (effectiveDisplay?.canGoPrev && effectiveDisplay?.goPrev) {
                    effectiveDisplay.goPrev();
                  }
                }}
                aria-label="Previous preview"
                disabled={!effectiveDisplay?.canGoPrev}
              >
                <ArrowLeftIcon />
              </button>
              <button
                type="button"
                className="preview-zoom__nav-button"
                onClick={(event) => {
                  event.stopPropagation();
                  if (effectiveDisplay?.canGoNext && effectiveDisplay?.goNext) {
                    effectiveDisplay.goNext();
                  }
                }}
                aria-label="Next preview"
                disabled={!effectiveDisplay?.canGoNext}
              >
                <ArrowRightIcon />
              </button>
            </div>
          ) : null}
        </div>
      </div>
    ),
    portalTarget,
  );
};

export default PreviewZoomOverlay;
