"use client";

import React, { createContext, useContext, useEffect, useMemo, useRef } from "react";
import Lenis from "lenis";
import { useMotionValue, type MotionValue } from "motion/react";

type ScrollContextValue = {
  lenis: Lenis | null;
  scrollY: MotionValue<number>; // in px
  progress: MotionValue<number>; // 0..1 for whole page
};

const ScrollContext = createContext<ScrollContextValue | null>(null);

type LenisScrollEvent = {
  scroll: number;
  limit: number;
  progress?: number;
};

export function useScrollContext() {
  const ctx = useContext(ScrollContext);
  if (!ctx) {
    throw new Error("useScrollContext must be used within <LenisProvider>");
  }
  return ctx;
}

export function LenisProvider({ children }: { children: React.ReactNode }) {
  const lenisRef = useRef<Lenis | null>(null);
  const scrollY = useMotionValue(0);
  const progress = useMotionValue(0);

  useEffect(() => {
    // Respect user preferences
    const prefersReducedMotion =
      typeof window !== "undefined" &&
      window.matchMedia &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    const lenis = new Lenis({
      // Softer, less "floaty" feel
      smoothWheel: !prefersReducedMotion,
      syncTouch: true,
      duration: 0.7, // was 1.2
      easing: (t: number) => 1 - Math.pow(1 - t, 2.4), // gentler ease-out
      wheelMultiplier: 0.9, // slightly reduce wheel amplitude
      // Use native on reduced motion
      wrapper: undefined,
      content: undefined,
      // If reduced motion, disable smoothing entirely
      lerp: prefersReducedMotion ? 1 : 0.22, // was 0.1 (heavier smoothing)
    });

    lenisRef.current = lenis;

    const onScroll = (e: unknown) => {
      const { scroll, limit, progress: p } = (e as LenisScrollEvent);
      scrollY.set(scroll);
      // Some versions of Lenis may not send progress, compute fallback if needed
      if (typeof p === "number" && Number.isFinite(p)) {
        progress.set(p);
      } else {
        const fallback =
          limit > 0
            ? Math.min(1, Math.max(0, scroll / limit))
            : 0;
        progress.set(fallback);
      }
    };

    lenis.on("scroll", onScroll);

    let rafId = 0;
    const raf = (time: number) => {
      lenis.raf(time);
      rafId = requestAnimationFrame(raf);
    };
    rafId = requestAnimationFrame(raf);

    // Initialize values
    onScroll({
      scroll: window.scrollY,
      limit: Math.max(0, document.documentElement.scrollHeight - window.innerHeight),
      progress: (document.documentElement.scrollHeight - window.innerHeight) > 0
        ? window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)
        : 0,
    });

    return () => {
      cancelAnimationFrame(rafId);
      lenis.off("scroll", onScroll);
      lenis.destroy();
      lenisRef.current = null;
    };
  }, [progress, scrollY]);

  const value = useMemo<ScrollContextValue>(() => {
    return {
      lenis: lenisRef.current,
      scrollY,
      progress,
    };
  }, [scrollY, progress]);

  return <ScrollContext.Provider value={value}>{children}</ScrollContext.Provider>;
}
