import {
  useRef,
  useLayoutEffect,
  useEffect,
  MutableRefObject,
  HTMLAttributes,
} from "react";
import { useRecoilState } from "recoil";
import { motion } from "framer-motion";

import { IconEntry } from "@/lib";
import { selectionEntryAtom } from "@/state";

interface IconGridItemProps extends HTMLAttributes<HTMLDivElement> {
  index: number;
  isDark: boolean;
  entry: IconEntry;
  originOffset: MutableRefObject<{ top: number; left: number }>;
}

const transition = { duration: 0.2 };
const originIndex = 0;
const delayPerPixel = 0.0004;

const itemVariants = {
  hidden: { opacity: 0 },
  visible: (delayRef: MutableRefObject<number>) => ({
    opacity: 1,
    transition: { delay: delayRef.current },
  }),
};

const IconGridItem = (props: IconGridItemProps) => {
  const { index, originOffset, entry, style } = props;
  const { name, Icon } = entry;
  const [selection, setSelectionEntry] = useRecoilState(selectionEntryAtom);
  const isOpen = selection?.name === name;
  const isNew = entry.tags.includes("*new*");
  const isUpdated = entry.tags.includes("*updated*");
  const delayRef = useRef<number>(0);
  const offset = useRef({ top: 0, left: 0 });
  const ref = useRef<any>();

  const handleOpen = () => setSelectionEntry(isOpen ? null : entry);

  // The measurement for all elements happens in the layoutEffect cycle
  // This ensures that when we calculate distance in the effect cycle
  // all elements have already been measured
  useLayoutEffect(() => {
    const element = ref.current;
    if (!element) return;

    offset.current = {
      top: element.offsetTop,
      left: element.offsetLeft,
    };

    if (index === originIndex) {
      originOffset.current = offset.current;
    }
  }, [index, originOffset]);

  useEffect(() => {
    const dx = Math.abs(offset.current.left - originOffset.current.left);
    const dy = Math.abs(offset.current.top - originOffset.current.top);
    const d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
    delayRef.current = d * delayPerPixel;
  }, [originOffset]);

  return (
    <>
      <motion.div
        className="grid-item"
        key={name}
        ref={ref}
        tabIndex={0}
        style={{
          ...style,
          backgroundColor: isOpen ? "var(--background-layer)" : undefined,
        }}
        custom={delayRef}
        transition={transition}
        variants={itemVariants}
        onKeyPress={(e) => e.key === "Enter" && handleOpen()}
        onClick={handleOpen}
      >
        <Icon />
        <p>
          {name}
          {isNew && <span className="badge new">•</span>}
          {isUpdated && <span className="badge updated">•</span>}
        </p>
      </motion.div>
    </>
  );
};

export default IconGridItem;
