import React, { CSSProperties, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { clamp } from '../utils/math';
import PdfViewer from '../preview/PdfViewer';
import MediaViewer from '../preview/MediaViewer';

import type { Document } from '../types/documents';

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

type NaturalSize = { width: number | null; height: number | null };
type FocusPoint = { xRatio: number; yRatio: number } | null;
type DisplayKind = 'image' | 'pdf';

type DocumentLink = {
  url: string;
  alt?: string;
  mimeType?: string | null;
};

type DocumentWithPreview = Document & { documentLink?: DocumentLink };

const determineDisplayKind = (entry?: DocumentLink | null): DisplayKind => {
  const type = entry?.mimeType?.toLowerCase?.() || '';
  if (type.includes('pdf')) {
    return 'pdf';
  }
  if (type.startsWith('image/')) {
    return 'image';
  }
  const url = entry?.url?.toLowerCase?.() || '';
  if (url.endsWith('.pdf')) {
    return 'pdf';
  }
  return 'image';
};

const noop = () => { };

const PreviewZoomOverlay: React.FC<PreviewZoomOverlayProps> = ({
  open = false,
  onClose = noop,
  document: overlayDocument = null,
}) => {
  const portalTarget = document.body;
  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 [documentSnapshot, setDocumentSnapshot] = useState<DocumentWithPreview | null>(null);
  const scrollRef = useRef<HTMLDivElement | null>(null);
  const mediaRef = 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);

  const currentDocument = overlayDocument;

  useEffect(() => {
    if (currentDocument?.documentLink?.url) {
      setDocumentSnapshot(currentDocument);
    }
  }, [currentDocument]);

  const activeDocument = open && currentDocument?.documentLink?.url ? currentDocument : documentSnapshot;
  const documentLink = activeDocument?.documentLink || null;
  const displayKind = useMemo(() => determineDisplayKind(documentLink), [documentLink]);
  const isPdfDisplay = displayKind === 'pdf';
  const documentTitle = activeDocument?.title || undefined;
  const effectiveAlt = documentLink?.alt || documentTitle || 'Document preview';

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

    if (open && documentLink?.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, documentLink?.url]);

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

  useEffect(() => {
  }, [isPdfDisplay, open, documentLink?.url]);

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


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

    const scrollEl = scrollRef.current;
    const mediaEl = mediaRef.current;
    if (!scrollEl || !mediaEl) {
      return;
    }

    const imageWidth = naturalSize.width || mediaEl.clientWidth;
    const imageHeight = naturalSize.height || mediaEl.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) {
      previouslyFocusedRef.current?.focus?.();
      previouslyFocusedRef.current = null;
      return;
    }

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

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

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

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

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

    return () => cancelAnimationFrame(frame);
  }, [renderBackdrop, documentLink?.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;
    }

  };

  const toggleZoomAtPoint = (clientX: number, clientY: number) => {
    if (isPdfDisplay) {
      return;
    }
    const media = mediaRef.current;
    setIsNativeScale((current) => {
      if (!current && media) {
        const rect = media.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 handleContentClick = (event: React.MouseEvent<HTMLElement>) => {
    if (isPdfDisplay) {
      return;
    }
    event.stopPropagation();
    toggleZoomAtPoint(event.clientX ?? 0, event.clientY ?? 0);
  };

  const shouldRender = renderBackdrop && Boolean(documentLink?.url);

  const stageClassName = isPdfDisplay
    ? 'preview-zoom__stage preview-zoom__stage--pdf'
    : 'preview-zoom__stage';

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

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

  const contentStyle: 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: 'calc(100vw - 2 * var(--preview-padding, 1.5vmin))',
      maxHeight: 'calc(100vh - 2 * var(--preview-padding, 1.5vmin))',
      touchAction: 'manipulation',
    };

  if (!shouldRender) {
    return null;
  }

  const effectiveDisplay = documentLink;

  return createPortal(
    (
      <div
        className={backdropClassName}
        role="dialog"
        aria-modal="true"
        aria-label="Enlarged document preview"
        onClick={onClose}
        onKeyDown={handleKeyDown}
      >
        <div
          className={stageClassName}
          onClick={(event) => {
            if (event.target === event.currentTarget) {
              onClose();
            }
          }}
        >
          <div
            className={containerClassName}
            ref={scrollRef}
            tabIndex={-1}
          >
            {isPdfDisplay ? (
              <div className="preview-zoom__pdf">
                <PdfViewer
                  src={effectiveDisplay.url}
                  title={effectiveAlt}
                  className="preview-zoom__pdf-viewer"
                  viewportRef={scrollRef}
                />
              </div>
            ) : (
              <MediaViewer
                src={effectiveDisplay.url}
                alt={effectiveAlt}
                className="preview-zoom__image"
                mediaRef={(node: HTMLElement | null) => {
                  mediaRef.current = node as HTMLImageElement;
                }}
                draggable={false}
                onLoad={(event) => {
                  const target = event.currentTarget as HTMLImageElement;
                  setNaturalSize({
                    width: target.naturalWidth || null,
                    height: target.naturalHeight || null,
                  });
                }}
                onClick={handleContentClick}
                style={contentStyle}
                mimeType={effectiveDisplay.mimeType || undefined}
              />
            )}
          </div>
        </div>
      </div>
    ),
    portalTarget,
  );
};

export default PreviewZoomOverlay;
