import {
  motion,
  useMotionValue,
  useTransform,
  useAnimationFrame,
} from 'framer-motion';
import styles from './Header.module.scss';
import Link from 'next/link';
import React, { useEffect, useRef, useState } from 'react';

interface Props {
  url: string;
  className: string;
  children: JSX.Element | JSX.Element[] | string | string[];
}

const dotVariants = {
  hover: {
    width: '172px',
    height: '172px',
    top: '-24px',
    right: '-30px',
    borderRadius: '86px',
    transition: {
      duration: 0.5,
    },
  },
  default: {
    width: '7px',
    height: '7px',
    top: '9.5px',
    right: '9.5px',
    borderRadius: '86px',
    transition: {
      duration: 0.5,
      delay: 0.25,
    },
  },
};

const textVariants = {
  hover: {
    opacity: 1,
    display: 'block',
    transition: {
      delay: 0.5,
    },
  },
  default: {
    opacity: 0,
    transitionEnd: {
      display: 'none',
    },
  },
};

const lerp = (a: number, b: number, n: number) => {
  return (1 - n) * a + n * b;
};

var distance = function distance(
  x1: number,
  y1: number,
  x2: number,
  y2: number
) {
  var a = x1 - x2;
  var b = y1 - y2;
  return Math.hypot(a, b);
}; // Generate a random float.

const DotLink = (props: Props): JSX.Element => {
  const { url, children, className } = props;
  const ref = useRef<HTMLDivElement>(null);
  const dotRef = useRef<HTMLSpanElement>(null);
  const [mousePositions, setMousePositions] = useState({ x: 0, y: -13 });
  const [mouseState, setMouseState] = useState({ hover: false });
  const rect = ref?.current?.getBoundingClientRect() as DOMRect;
  const {
    left = 0,
    width = 0,
    top = 0,
    height = 0,
  } = rect || { left: 0, width: 0, top: 0, height: 0 };
  const x = useMotionValue(mousePositions.x - left - width / 2 + 13);
  const y = useMotionValue(-13 - top - height / 2);

  const translateX = useTransform(x, [3.5, 23], [3.5, 23]);
  const translateY = useTransform(y, [-23, -3.5], [-23, -3.5]);
  const mouseMove = (ev: MouseEvent) => {
    setMousePositions({
      x: ev.clientX,
      y: ev.clientY,
    });
  };

  useEffect(() => {
    window.addEventListener('mousemove', mouseMove);
    return () => {
      window.removeEventListener('mousemove', mouseMove);
    };
  }, []);

  const handleMouse = () => {
    const distanceMouseButton = distance(
      mousePositions.x,
      mousePositions.y,
      left + width / 2,
      top + height / 2
    );

    let newX =
      distanceMouseButton < width * 1 && !mouseState.hover
        ? lerp(
            translateX.getPrevious(),
            mousePositions.x - left - width / 2 + 13,
            0.05
          )
        : lerp(translateX.getPrevious(), 13, 0.05);
    let newY =
      distanceMouseButton < height * 1 && !mouseState.hover
        ? lerp(translateY.getPrevious(), mousePositions.y - top - height, 0.05)
        : lerp(translateY.getPrevious(), -13, 0.05);

    x.set(newX);
    y.set(newY);
  };

  useAnimationFrame(() => {
    handleMouse();
  });

  return (
    <motion.div
      ref={ref}
      className={styles.dotContainer}
      initial="default"
      whileHover="hover"
      onHoverStart={() => {
        setMouseState({ hover: true });
      }}
      onHoverEnd={() => {
        setMouseState({ hover: false });
      }}
    >
      <motion.div
        style={{
          translateX,
          translateY,
        }}
      >
        <motion.span className={styles.dot} variants={dotVariants} ref={dotRef}>
          <Link href={url} passHref={true} legacyBehavior>
            <motion.a
              className={`${styles.text} ${className}`}
              data-label={children}
              variants={textVariants}
            >
              {children}
            </motion.a>
          </Link>
        </motion.span>
      </motion.div>
    </motion.div>
  );
};

export default DotLink;
