/** پیدا کردن المان `.page` با شناسهٔ مشخص (حالت تک‌صفحه‌ای موبایل) */
export function findPageById(root: HTMLElement, pageId: number): HTMLElement | null {
  const page = root.querySelector<HTMLElement>(`.page[data-page-id="${pageId}"]`);
  if (!(page instanceof HTMLElement) || !root.contains(page) || !isPageVisible(page)) {
    return null;
  }
  return page;
}

/** پیدا کردن المان `.page` زیر مختصات مشخص، با اولویت بالاترین z-index در حالت اسپرد */
export function findPageAtPoint(
  root: HTMLElement,
  clientX: number,
  clientY: number,
  ignoreRoot?: HTMLElement | null,
): HTMLElement | null {
  const hitElements = document.elementsFromPoint(clientX, clientY);
  for (const el of hitElements) {
    if (!(el instanceof HTMLElement)) continue;
    if (ignoreRoot?.contains(el)) continue;
    if (!root.contains(el)) continue;

    const page = el.closest(".page");
    if (page instanceof HTMLElement && root.contains(page) && isPageVisible(page)) {
      return page;
    }
  }

  // fallback برای زمانی که pointer-events روی صفحات محدود شده (مثلاً هنگام ورق‌خوردن)
  let match: HTMLElement | null = null;
  let topZ = -Infinity;

  root.querySelectorAll<HTMLElement>(".page").forEach((page) => {
    if (!isPageVisible(page)) return;

    const rect = page.getBoundingClientRect();
    if (clientX < rect.left || clientX > rect.right || clientY < rect.top || clientY > rect.bottom) {
      return;
    }

    const z = getPageStackZIndex(page);
    if (z >= topZ) {
      topZ = z;
      match = page;
    }
  });

  return match;
}

function isPageVisible(page: HTMLElement): boolean {
  const rect = page.getBoundingClientRect();
  if (rect.width < 1 || rect.height < 1) return false;

  const style = window.getComputedStyle(page);
  if (style.display === "none" || style.visibility === "hidden") return false;

  const opacity = Number.parseFloat(style.opacity);
  if (Number.isFinite(opacity) && opacity < 0.05) return false;

  return true;
}

function getPageStackZIndex(page: HTMLElement): number {
  let el: HTMLElement | null = page;
  let maxZ = 0;

  while (el) {
    const z = Number.parseInt(window.getComputedStyle(el).zIndex, 10);
    if (Number.isFinite(z)) {
      maxZ = Math.max(maxZ, z);
    }
    el = el.parentElement;
  }

  return maxZ;
}

export type MagnifierGeometry = {
  /** فاصلهٔ گوشهٔ بالا-چپ صفحه از containerRef (برای موقعیت‌دهی خود لنز) */
  pageOffsetX: number;
  pageOffsetY: number;
  /** مختصات موس نسبت به گوشهٔ بالا-چپ صفحه (بدون کلمپ — برای محتوای داخل لنز) */
  localX: number;
  localY: number;
  /** مختصات کلمپ‌شده در محدودهٔ صفحه (برای موقعیت خود لنز، تا از صفحه بیرون نزند) */
  clampedX: number;
  clampedY: number;
  pageWidth: number;
  pageHeight: number;
};

export function computeMagnifierGeometry(
  clientX: number,
  clientY: number,
  page: HTMLElement,
  container: HTMLElement,
  lensSize: number,
): MagnifierGeometry | null {
  const containerRect = container.getBoundingClientRect();
  const pageRect = page.getBoundingClientRect();

  if (pageRect.width <= 0 || pageRect.height <= 0) {
    return null;
  }

  const localX = clientX - pageRect.left;
  const localY = clientY - pageRect.top;

  if (localX < 0 || localY < 0 || localX > pageRect.width || localY > pageRect.height) {
    return null;
  }

  const half = lensSize / 2;
  const clampedX =
    pageRect.width <= lensSize ? pageRect.width / 2 : Math.max(half, Math.min(pageRect.width - half, localX));
  const clampedY =
    pageRect.height <= lensSize ? pageRect.height / 2 : Math.max(half, Math.min(pageRect.height - half, localY));

  return {
    pageOffsetX: pageRect.left - containerRect.left,
    pageOffsetY: pageRect.top - containerRect.top,
    localX,
    localY,
    clampedX,
    clampedY,
    pageWidth: pageRect.width,
    pageHeight: pageRect.height,
  };
}
