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

const noop = () => {};

const clamp = (value, min, max) => {
  if (value < min) return min;
  if (value > max) return max;
  return value;
};

const ensureDocumentRoot = () => {
  if (typeof document === 'undefined') {
    return null;
  }
  return document.body;
};

const CLICK_DELAY_MS = 240;

const PreviewZoomOverlay = ({
  open = false,
  display = null,
  onClose = noop,
}) => {
  const portalTarget = ensureDocumentRoot();
  const [isNativeScale, setIsNativeScale] = useState(false);
  const [naturalSize, setNaturalSize] = useState({ width: null, height: null });
  const [renderBackdrop, setRenderBackdrop] = useState(false);
  const [isBackdropVisible, setBackdropVisible] = useState(false);
  const [displaySnapshot, setDisplaySnapshot] = useState(null);
  const scrollRef = useRef(null);
  const imageRef = useRef(null);
  const focusRef = useRef(null);
  const previouslyFocusedRef = useRef(null);
  const visibilityTimerRef = useRef(null);
  const displayTimerRef = useRef(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 = 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 (!open) {
      if (previouslyFocusedRef.current && typeof previouslyFocusedRef.current.focus === 'function') {
        previouslyFocusedRef.current.focus();
      }
      previouslyFocusedRef.current = null;
      return;
    }

    if (typeof document !== 'undefined') {
      const active = document.activeElement;
      if (active && typeof active.focus === 'function') {
        previouslyFocusedRef.current = active;
      } else {
        previouslyFocusedRef.current = null;
      }
    }
  }, [open]);

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

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

    const frame = requestAnimationFrame(() => {
      const scrollEl = scrollRef.current;
      if (scrollEl && typeof scrollEl.focus === 'function') {
        scrollEl.focus({ preventScroll: true });
      }
    });

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

  const handleKeyDown = (event) => {
    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, clientY) => {
    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 pointerTapHandler = usePointerTap({
    delay: CLICK_DELAY_MS,
    onSingle: () => {
      onClose();
    },
    onDouble: ({ clientX, clientY }) => {
      toggleZoomAtPoint(clientX, clientY);
    },
  });

  const handleImagePointerDown = (event) => {
    event.stopPropagation();
    pointerTapHandler(event);
  };

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

  const effectiveDisplay = activeDisplay;
  const navVisible = Boolean(effectiveDisplay?.canGoPrev || effectiveDisplay?.canGoNext);
  const stageClassName = [
    'preview-zoom__stage',
  ]
    .filter(Boolean)
    .join(' ');

  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 = 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}
        onClick={(event) => event.stopPropagation()}
        onKeyDown={handleKeyDown}
      >
        <div
          className={containerClassName}
          ref={scrollRef}
          tabIndex={-1}
        >
          <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,
              });
            }}
            onPointerDown={handleImagePointerDown}
            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;
