/* eslint-disable react-hooks/rules-of-hooks */
"use client";

import { useRef, useState } from "react";

import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { theme } from "@/lib/theme";
import { cn, colorAlpha } from "@/lib/utils";
import {
  interpolate,
  motion,
  SpringOptions,
  useMotionValue,
  useMotionValueEvent,
  useSpring,
  useTransform,
} from "framer-motion";

// Sigmoid-based decay function
export function decay(value: number, max: number) {
  if (max === 0) {
    return 0;
  }

  let entry = value / max;
  let sigmoid = 2 * (1 / (1 + Math.exp(-entry)) - 0.5);

  return sigmoid * max;
}

type SnapFunction = (value: number) => number;

function snap(points: number | number[], threshold: number): SnapFunction {
  if (Array.isArray(points)) {
    // Calculate the snap points for a list of points
    const snapPoints: number[] = [
      points[0] + threshold * (points[1] - points[0]),
    ];
    for (let i = 1; i < points.length - 1; i++) {
      const snapPoint = points[i] + threshold * (points[i + 1] - points[i]);
      snapPoints.push(snapPoint);
    }

    return (value: number) => {
      for (let i = 0; i < snapPoints.length; i++) {
        if (value <= snapPoints[i]) {
          return points[i];
        }
      }
      return points[points.length - 1];
    };
  } else {
    // Calculate the snap point for a single number
    const snapPoint = threshold * points;

    return (value: number) => {
      if (value <= snapPoint) {
        return 0; // Assuming we snap to 0 if below threshold
      }
      return points;
    };
  }
}

const LIST = {
  HEIGHT: 48,
  GAP: 0,
  ITEMS: [
    {
      id: 1,
      title: "Person 1",
    },
    {
      id: 2,
      title: "Person 2",
    },
    {
      id: 3,
      title: "Person 3",
    },
    {
      id: 4,
      title: "Person 4",
    },
    {
      id: 5,
      title: "Person 5",
    },
    {
      id: 6,
      title: "Person 6",
    },
  ],
};

type Position = {
  x: number;
  y: number;
  width: number;
  height: number;
  offset: {
    y: number;
  };
};

const Menu = () => {
  const [open, setOpen] = useState(false);
  const [activeIndex, setActiveIndex] = useState<number>(-1);
  const [direction, setDirection] = useState<number>(0);

  const containerRef = useRef<HTMLDivElement>(null);

  const [transit, setTransit] = useState(false);

  const indexY = useMotionValue(0);

  const selectorY = useMotionValue(0);

  const stickyMotionX = useMotionValue(0);
  const stickyMotionY = useMotionValue(0);
  const [stickyY, setStickyY] = useState(0);
  const [stickyX, setStickyX] = useState(0);

  const [isOverflow, setIsOverflow] = useState(false);

  const motionOverflowAmount = useMotionValue(0);
  const [overflowAmount, setOverflowAmount] = useState(0);
  const [interpolatedOverflow, setInterpolatedOverflow] = useState(0);

  useMotionValueEvent(stickyMotionX, "change", (v) => {
    setStickyX(v);
  });
  useMotionValueEvent(stickyMotionY, "change", (v) => {
    setStickyY(v);
  });

  useMotionValueEvent(motionOverflowAmount, "change", (v) => {
    setOverflowAmount(v);
  });

  const springConfig: SpringOptions = {
    mass: 0.8,
    stiffness: 300,
    damping: 26,
  };

  // Add a ref to store positions of each list item
  const itemPositions = useRef<Position[]>([]);
  const itemRefs = useRef<(HTMLDivElement | null)[]>([]);

  const indexSnapPoints = Array.from(
    { length: LIST.ITEMS.length },
    (_, i) => i,
  );

  const handleInteraction = (clientX: number, clientY: number) => {
    if (containerRef.current) {
      const rect = containerRef.current.getBoundingClientRect();
      const newIndexX = clientX - rect.x;
      const newIndexY = clientY - rect.y;

      // New algorithm for direction
      const prevIndexY = indexY.get();
      const directionThreshold = 0; // Adjust this value as needed
      if (Math.abs(newIndexY - prevIndexY) > directionThreshold) {
        setDirection(newIndexY > prevIndexY ? 1 : -1);
      }

      indexY.set(newIndexY);

      let interpolatedIndex = interpolate(
        [0, rect.height],
        [0, LIST.ITEMS.length],
        {
          clamp: true,
        },
      );

      const snapTo = snap(indexSnapPoints, 1);
      const value = interpolatedIndex(indexY.get());

      setActiveIndex(snapTo(value));
      console.log(
        "snap",
        snapTo(value),
        value.toFixed(2),
        "direction",
        direction,
      );

      // selectorY
      if (activeIndex !== -1) {
        selectorY.set(
          itemPositions.current[activeIndex].offset.y +
            (Math.abs(overflowAmount) > 0
              ? decay(interpolate([-10, 10], [-10, 10])(overflowAmount), 6)
              : 0),
        );

        stickyMotionY.set(
          indexY.get() - itemPositions.current[activeIndex].offset.y,
        );
        stickyMotionX.set(newIndexX);

        setTimeout(() => {
          setTransit(true);
        }, 50);

        // Overflow
        if (indexY.get() > rect.height || indexY.get() < 0) {
          setIsOverflow(true);
          if (indexY.get() > rect.height) {
            motionOverflowAmount.set(indexY.get() - rect.height);
          } else {
            motionOverflowAmount.set(indexY.get());
          }
        } else {
          setIsOverflow(false);
          motionOverflowAmount.set(0);
        }
      }
    }
  };

  const scaleYSpring = useSpring(
    useTransform(
      motionOverflowAmount,
      (v) => 1 + decay(interpolate([0, 50], [0, 0.25])(Math.abs(v)), 0.1),
    ),
    springConfig,
  );

  const ySpring = useSpring(
    useTransform(selectorY, (v) => {
      return v + interpolate([0, 50], [-3, 4])(stickyY) + 0;
    }),
    transit ? springConfig : { duration: 0 },
  );

  const xSpring = useSpring(
    useTransform(stickyMotionX, (v) => {
      return interpolate([0, 206], [-3, 4])(stickyX);
    }),
    transit ? springConfig : { duration: 0 },
  );

  const itemSprings = LIST.ITEMS.map((_, index) => ({
    y: useSpring(
      useTransform(stickyMotionY, (v) => {
        return activeIndex === index
          ? interpolate([0, 50], [-1, 1])(stickyY)
          : 0;
      }),
      springConfig,
    ),
    x: useSpring(
      useTransform(stickyMotionX, (v) => {
        return activeIndex === index
          ? interpolate([0, 206], [-1, 1])(stickyX)
          : 0;
      }),
      springConfig,
    ),
    scaleY: useSpring(
      useTransform(motionOverflowAmount, (v) =>
        activeIndex === index
          ? 1 + decay(interpolate([0, 50], [0, 0.25])(Math.abs(v)), 1)
          : 1,
      ),
      springConfig,
    ),
  }));

  return (
    <Popover open={open}>
      <div className="flex flex-col items-center space-y-4">
        <PopoverContent
          className="flex w-auto touch-none rounded-3xl border bg-neutral-50 p-0 shadow-none"
          side="top"
        >
          <motion.div
            ref={containerRef}
            className="relative flex w-56 flex-col items-center justify-center divide-y rounded-3xl p-2 shadow-sm"
            style={{
              gap: LIST.GAP,
              boxSizing: "border-box",
            }}
            onPointerEnter={(event) =>
              (event.target as HTMLDivElement).setPointerCapture(
                event.pointerId,
              )
            }
          >
            <div className="flex w-full">
              <motion.div
                className="bgChonk absolute -inset-x-2 inset-y-0 z-[1] rounded-2xl border bg-white"
                initial={{
                  opacity: 0,
                  scale: 0.7,
                }}
                animate={{
                  // backgroundColor: isOverflow
                  //   ? theme.colors.orange[50]
                  //   : theme.colors.white,
                  opacity: activeIndex !== -1 && transit ? 1 : 0,
                  scale: activeIndex !== -1 && transit ? 1 : 0.7,
                  transition: {
                    type: "spring",
                    stiffness: 600,
                    damping: 30,
                    mass: 0.25,
                  },
                }}
                style={{
                  scaleY: scaleYSpring,
                  originY: overflowAmount < 0 ? "bottom" : "top",
                  height: LIST.HEIGHT,
                  y: ySpring,
                  x: xSpring,
                }}
              ></motion.div>
            </div>
            {LIST.ITEMS.map((item, index) => {
              const springs = itemSprings[index];
              return (
                <motion.div
                  ref={(el) => {
                    itemRefs.current[index] = el;
                    if (el) {
                      const rect = el.getBoundingClientRect();
                      itemPositions.current[index] = {
                        x: rect.x,
                        y: rect.y,
                        width: rect.width,
                        height: rect.height,
                        offset: {
                          y: el.offsetTop,
                        },
                      };
                    }
                  }}
                  key={item.id}
                  className={cn(
                    "relative flex touch-none select-none flex-row items-center justify-start gap-4 border border-blue-400/0 px-2 transition-[transform-origin] duration-500",
                  )}
                  style={{
                    width: "100%",
                    height: LIST.HEIGHT,
                    zIndex: 2,
                    y: springs.y,
                    x: springs.x,
                    scaleY: springs.scaleY,
                    originY:
                      overflowAmount > 0
                        ? "top"
                        : overflowAmount < 0
                          ? "bottom"
                          : "center",
                    // originY:
                    //   indexY.get() >
                    //   (containerRef.current?.clientHeight || 0) / 2
                    //     ? "top"
                    //     : "bottom",
                  }}
                  animate={{
                    scale: activeIndex === index ? 1.125 : 1,
                    transition: {
                      type: "spring",
                      mass: 1.4,
                      stiffness: 300,
                      damping: 28,
                    },
                  }}
                >
                  <div className="z-[2] flex flex-row items-center justify-start gap-4">
                    <div className="flex aspect-square h-6 rounded-full bg-blue-400"></div>
                    <div>{item.title}</div>
                    <div className="flex flex-row gap-4 text-xs">
                      {/* <motion.div>
                      {useTransform(indexY, (v) => v.toFixed(0))}
                    </motion.div>
                    <motion.div>
                      {useTransform(motionOverflowAmount, (v) => v.toFixed(0))}
                    </motion.div> */}
                    </div>
                  </div>
                </motion.div>
              );
            })}
          </motion.div>
        </PopoverContent>
        <PopoverTrigger className="touch-none outline-none">
          <motion.div
            className="bg-neutral-4000/10 w-auto select-none rounded-xl border px-4 py-2 shadow-sm"
            whileHover={{
              backgroundColor: colorAlpha(theme.colors.neutral[500], 0.2),
            }}
            whileTap={{
              backgroundColor: colorAlpha(theme.colors.neutral[500], 0.2),
            }}
            // onPointerDown={(event) => {
            //   (event.target as HTMLDivElement).setPointerCapture(
            //     event.pointerId
            //   );
            //   setTimeout(() => {
            //     open === false && setOpen(true);
            //   }, 800);
            // }}
            // onLostPointerCapture={() => {
            //   setOpen(false);
            //   setActiveIndex(-1);
            //   setTimeout(() => {
            //     setTransit(false);
            //   }, 50);
            //   setDirection(0);
            //   console.log("end");
            // }}
            onPanStart={(_, info) => {
              handleInteraction(info.point.x, info.point.y);
              info.offset.y < 0 &&
                setTimeout(() => {
                  setOpen(true);
                }, 100);
            }}
            onPan={(_, info) => {
              handleInteraction(info.point.x, info.point.y);
            }}
            onPanEnd={() => {
              setOpen(false);
              setActiveIndex(-1);
              setTimeout(() => {
                setTransit(false);
              }, 50);
              setDirection(0);
              console.log("end");
            }}
          >
            Swipe Up to Share
          </motion.div>
        </PopoverTrigger>
      </div>
    </Popover>
  );
};

export default Menu;


the client relative mouse position make it parent relative. fix it. whether parent is justify center flex or any alignment of the menu button. the selector should always be at cursor position.