import { type RefObject, useCallback, useEffect, useLayoutEffect, useRef } from "react";
import { MAGNIFIER_SIZE, ZOOM_SCALE } from "./constants";
import { computeMagnifierGeometry, findPageAtPoint } from "./magnifierUtils";

type UseMagnifierOptions = {
  enabled: boolean;
  /** ناحیهٔ موقعیت‌دهی لنز (مرجع مختصات نسبی) */
  containerRef: RefObject<HTMLElement | null>;
  /** ناحیهٔ تعامل و جستجوی صفحات (wrapper کتاب) */
  interactionRef: RefObject<HTMLElement | null>;
  overlayRef: RefObject<HTMLElement | null>;
  lensRef: RefObject<HTMLElement | null>;
  /** لایهٔ داخل لنز که محتوای صفحه (BookPage با مقیاس بزرگ) داخلش رندر می‌شود */
  mirrorRef: RefObject<HTMLElement | null>;
  /** با تغییر آن (ورق خوردن) وضعیت فعلی ریست می‌شود */
  pageIndex?: number;
  /** وقتی صفحهٔ زیر ماوس عوض شود صدا زده می‌شود تا محتوای مناسب رندر شود */
  onActivePageChange: (pageId: number | null) => void;
};

export function useMagnifier({
  enabled,
  containerRef,
  interactionRef,
  overlayRef,
  lensRef,
  mirrorRef,
  pageIndex,
  onActivePageChange,
}: UseMagnifierOptions) {
  const rafIdRef = useRef<number | null>(null);
  const pendingPointRef = useRef<{ x: number; y: number } | null>(null);
  const lastPageIdRef = useRef<number | null>(null);

  const hideLens = useCallback(() => {
    const lens = lensRef.current;
    if (lens) {
      lens.style.visibility = "hidden";
    }
  }, [lensRef]);

  const resetActivePage = useCallback(() => {
    if (lastPageIdRef.current !== null) {
      lastPageIdRef.current = null;
      onActivePageChange(null);
    }
  }, [onActivePageChange]);

  // با تغییر صفحه (ورق خوردن) باید دوباره از صفر hit-test انجام شود
  useEffect(() => {
    if (!enabled) return;
    pendingPointRef.current = null;
    hideLens();
    resetActivePage();
  }, [pageIndex, enabled, hideLens, resetActivePage]);

  useLayoutEffect(() => {
    if (!enabled) {
      pendingPointRef.current = null;
      if (rafIdRef.current != null) {
        cancelAnimationFrame(rafIdRef.current);
        rafIdRef.current = null;
      }
      hideLens();
      resetActivePage();
      return;
    }

    const container = containerRef.current;
    const interaction = interactionRef.current;
    if (!container || !interaction) return;

    const half = MAGNIFIER_SIZE / 2;

    const applyGeometry = (clientX: number, clientY: number) => {
      const lens = lensRef.current;
      if (!lens) return;

      const page = findPageAtPoint(interaction, clientX, clientY, overlayRef.current);
      if (!page) {
        hideLens();
        resetActivePage();
        return;
      }

      const geometry = computeMagnifierGeometry(clientX, clientY, page, container, MAGNIFIER_SIZE);
      if (!geometry) {
        hideLens();
        return;
      }

      const pageIdAttr = page.dataset.pageId;
      const pageId = pageIdAttr != null ? Number(pageIdAttr) : null;
      if (pageId !== lastPageIdRef.current) {
        lastPageIdRef.current = pageId;
        onActivePageChange(pageId);
      }

      const { pageOffsetX, pageOffsetY, localX, localY, clampedX, clampedY } = geometry;

      lens.style.visibility = "visible";
      lens.style.transform = `translate3d(${pageOffsetX + clampedX - half}px, ${pageOffsetY + clampedY - half}px, 0)`;

      const mirror = mirrorRef.current;
      if (mirror) {
        const tx = half - localX * ZOOM_SCALE;
        const ty = half - localY * ZOOM_SCALE;
        mirror.style.transform = `translate3d(${tx}px, ${ty}px, 0)`;
      }
    };

    const flushPending = () => {
      rafIdRef.current = null;
      const point = pendingPointRef.current;
      if (!point) return;
      applyGeometry(point.x, point.y);
    };

    const scheduleUpdate = (clientX: number, clientY: number) => {
      pendingPointRef.current = { x: clientX, y: clientY };
      if (rafIdRef.current == null) {
        rafIdRef.current = requestAnimationFrame(flushPending);
      }
    };

    const handleMouseMove = (event: MouseEvent) => {
      scheduleUpdate(event.clientX, event.clientY);
    };

    const handleMouseLeave = () => {
      pendingPointRef.current = null;
      hideLens();
      resetActivePage();
    };

    interaction.addEventListener("mousemove", handleMouseMove);
    interaction.addEventListener("mouseleave", handleMouseLeave);

    return () => {
      interaction.removeEventListener("mousemove", handleMouseMove);
      interaction.removeEventListener("mouseleave", handleMouseLeave);
      if (rafIdRef.current != null) {
        cancelAnimationFrame(rafIdRef.current);
        rafIdRef.current = null;
      }
      hideLens();
      resetActivePage();
    };
  }, [enabled, containerRef, interactionRef, overlayRef, lensRef, mirrorRef, hideLens, resetActivePage, onActivePageChange]);

  // بلافاصله بعد از سوییچ صفحه (رندر مجدد mirror) آخرین موقعیت شناخته‌شده را دوباره اعمال می‌کند
  // تا محتوای تازه‌رندرشده یک فریم با موقعیت اشتباه دیده نشود.
  useLayoutEffect(() => {
    if (!enabled) return;
    const mirror = mirrorRef.current;
    const point = pendingPointRef.current;
    if (!mirror || !point) return;
    const half = MAGNIFIER_SIZE / 2;
    const container = containerRef.current;
    const interaction = interactionRef.current;
    if (!container || !interaction) return;
    const page = findPageAtPoint(interaction, point.x, point.y, overlayRef.current);
    if (!page) return;
    const geometry = computeMagnifierGeometry(point.x, point.y, page, container, MAGNIFIER_SIZE);
    if (!geometry) return;
    mirror.style.transform = `translate3d(${half - geometry.localX * ZOOM_SCALE}px, ${half - geometry.localY * ZOOM_SCALE}px, 0)`;
  });
}
