"use client";

import { useEffect, useMemo, useRef } from "react";
import { useMotionValue, type MotionValue } from "motion/react";
import { useScrollContext } from "@/app/providers/LenisProvider";

/**
 * Math utilities
 */
export function clamp(n: number, min = 0, max = 1) {
  return Math.min(max, Math.max(min, n));
}

export function lerp(a: number, b: number, t: number) {
  return a + (b - a) * t;
}

export function mapRange(
  inMin: number,
  inMax: number,
  outMin: number,
  outMax: number,
  v: number,
  clampOutput = true,
) {
  const t = (v - inMin) / (inMax - inMin || 1);
  const m = outMin + (outMax - outMin) * t;
  return clampOutput ? clamp(m, Math.min(outMin, outMax), Math.max(outMin, outMax)) : m;
}

/**
 * Returns a MotionValue<number> that represents normalized progress [0..1]
 * for the given section element as it scrolls through the viewport.
 *
 * Progress is 0 when the section just touches the bottom of viewport and
 * 1 when it has completely exited at the top. The range used is
 * (section height + viewport height) to distribute progress smoothly.
 */
export function useSectionProgress<T extends HTMLElement>(
  ref: React.RefObject<T>,
): MotionValue<number> {
  const { scrollY } = useScrollContext();
  const progress = useMotionValue(0);
  const viewportHRef = useRef<number>(typeof window !== "undefined" ? window.innerHeight : 0);

  useEffect(() => {
    const onResize = () => {
      viewportHRef.current = window.innerHeight;
      // force update after resize
      update();
    };
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const update = () => {
    const el = ref.current;
    if (!el) return;
    const rect = el.getBoundingClientRect();
    const viewport = viewportHRef.current || window.innerHeight || 0;
    const range = rect.height + viewport;
    // 0 when bottom of viewport touches top of element (rect.top === viewport)
    // 1 when element's bottom crosses top of viewport (rect.bottom <= 0)
    const value = clamp((viewport - rect.top) / (range || 1), 0, 1);
    progress.set(value);
  };

  useEffect(() => {
    // Subscribe to Lenis-driven scroll updates
    const unsub = (scrollY as MotionValue<number>).on("change", update);
    // Initialize
    update();
    return () => {
      unsub();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [ref, scrollY]);

  return progress;
}

/**
 * Hook to compute parallax offset (in px) tied to page scroll for a given element.
 * speed: positive moves with scroll (slower if between 0..1), negative moves opposite.
 * axis: "y" or "x"
 */
export function useParallax<T extends HTMLElement>(ref: React.RefObject<T>, speed = 0.2, axis: "y" | "x" = "y") {
  const { scrollY } = useScrollContext();
  const offset = useMotionValue(0);
  const baseRef = useRef<number | null>(null);

  const measureBase = () => {
    const el = ref.current;
    if (!el) return;
    const rect = el.getBoundingClientRect();
    // document scroll position
    const docScroll = typeof window !== "undefined" ? window.scrollY : 0;
    baseRef.current = rect.top + docScroll;
  };

  const update = (sy: number) => {
    if (baseRef.current === null) measureBase();
    if (baseRef.current === null) return;
    const d = sy - baseRef.current;
    offset.set(d * speed);
  };

  useEffect(() => {
    // Measure on mount and on resize to handle layout shifts
    measureBase();
    const onResize = () => {
      baseRef.current = null;
      measureBase();
      update((scrollY as MotionValue<number>).get());
    };
    window.addEventListener("resize", onResize);
    const unsub = (scrollY as MotionValue<number>).on("change", update);

    // Initialize
    update((scrollY as MotionValue<number>).get());

    return () => {
      window.removeEventListener("resize", onResize);
      unsub();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [ref, speed, axis, scrollY]);

  return useMemo(
    () => ({
      [axis]: offset,
    }),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [axis, offset],
  ) as { x?: MotionValue<number>; y?: MotionValue<number> };
}
