import { animated, useSpring as useThreeSpring } from "@react-spring/three";
import { useDisableRaycast } from "../hooks/useDisableRaycast";
import { View, viewToHash } from "@/components/scene/consts";
import Stack from "@/util/stack";
import {
  type Dispatch,
  type SetStateAction,
  useCallback,
  useEffect,
  useState,
} from "react";
import {
  type MeshStandardMaterial,
  type BufferGeometry,
  BackSide,
} from "three";
import { Billboard, Text } from "@react-three/drei";

interface ButtonsProps {
  menuTraversal: Stack<View>;
  setMenuTraversal: Dispatch<SetStateAction<Stack<View>>>;
  pendingView: View | null;
  setPendingView: Dispatch<SetStateAction<View | null>>;
  goPreviousView(): void;
  currentView: View;
}

const AnimatedText = animated(Text);

export const Buttons = ({
  menuTraversal,
  setMenuTraversal,
  pendingView,
  setPendingView,
  goPreviousView,
  currentView,
}: ButtonsProps) => {
  const [hovered, setHovered] = useState<View | null>(null);

  const desktopRef = useDisableRaycast(currentView !== View.MainView);
  const printerRef = useDisableRaycast(currentView !== View.MainView);
  const sideRef = useDisableRaycast(currentView !== View.MainView);
  const cellphoneRef = useDisableRaycast(currentView !== View.DesktopView);
  const pcRef = useDisableRaycast(currentView !== View.DesktopView);

  const goToView = useCallback(
    (view: View) => {
      setMenuTraversal((prev) => {
        const next = prev.clone();
        next.push(view);
        return next;
      });
      setPendingView(view);
    },
    [setMenuTraversal, setPendingView]
  );

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") goPreviousView();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [goPreviousView]);

  useEffect(() => {
    const onPop = () => {
      goPreviousView();
    };
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, [goPreviousView]);

  useEffect(() => {
    if (pendingView === null) return;
    window.history.pushState(
      { depth: menuTraversal.size() },
      "",
      viewToHash[pendingView] ? `#${viewToHash[pendingView]}` : "/"
    );
    setPendingView(null);
  }, [menuTraversal, pendingView, setPendingView]);

  useEffect(() => {
    if (hovered) {
      document.body.style.cursor = "pointer";
    } else {
      document.body.style.cursor = "auto";
    }
  }, [hovered]);

  const mainMenuSpring = useThreeSpring<MeshStandardMaterial>({
    opacity: currentView === View.MainView ? 1 : 0,
  });

  const desktopMenuSpring = useThreeSpring<MeshStandardMaterial>({
    opacity: currentView === View.DesktopView ? 1 : 0,
  });

  const creditsButtonSpring = useThreeSpring<MeshStandardMaterial>({
    opacity:
      currentView === View.DesktopView
        ? hovered === View.CreditsView
          ? 1
          : 0.25
        : 0,
  });

  const desktopViewSpring = useThreeSpring<BufferGeometry>({
    scale: hovered === View.DesktopView ? 1.25 : 1,
  });
  const resumeViewSpring = useThreeSpring<BufferGeometry>({
    scale: hovered === View.ResumeView ? 1.25 : 1,
  });
  const cellphoneViewSpring = useThreeSpring<BufferGeometry>({
    scale: hovered === View.CellphoneView ? 1.25 : 1,
  });
  const pcViewSpring = useThreeSpring<BufferGeometry>({
    scale: hovered === View.PCView ? 1.25 : 1,
  });
  const creditsViewSpring = useThreeSpring<BufferGeometry>({
    scale: hovered === View.CreditsView ? 1.25 : 1,
  });
  const printerViewSpring = useThreeSpring<BufferGeometry>({
    scale: hovered === View.PrinterView ? 1.25 : 1,
  });

  return (
    <>
      {/* Main Menu */}
      <group>
        <animated.mesh
          ref={desktopRef}
          position={[0.9, 7, 1.65]}
          onClick={
            currentView === View.MainView
              ? () => goToView(View.DesktopView)
              : undefined
          }
          onPointerOver={
            currentView === View.MainView
              ? () => setHovered(View.DesktopView)
              : undefined
          }
          onPointerOut={() => setHovered(null)}
          scale={desktopViewSpring.scale}
        >
          <sphereGeometry args={[0.6, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="white"
            opacity={mainMenuSpring.opacity}
          />
        </animated.mesh>
        <animated.mesh
          position={[0.9, 7, 1.65]}
          scale={desktopViewSpring.scale}
        >
          <sphereGeometry args={[0.65, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="black"
            opacity={mainMenuSpring.opacity}
            side={BackSide}
          />
        </animated.mesh>
        <Billboard position={[0.9, 8.5, 1.65]}>
          <AnimatedText
            font="/assets/inter.ttf"
            fontSize={0.75}
            outlineWidth={0.05}
            fillOpacity={mainMenuSpring.opacity}
            outlineOpacity={mainMenuSpring.opacity}
          >
            Projects & Socials
          </AnimatedText>
        </Billboard>
        <animated.mesh
          ref={printerRef}
          position={[-5.5, 6, -2]}
          onClick={
            currentView === View.MainView
              ? () => goToView(View.PrinterView)
              : undefined
          }
          onPointerOver={
            currentView === View.MainView
              ? () => setHovered(View.PrinterView)
              : undefined
          }
          onPointerOut={() => setHovered(null)}
          scale={printerViewSpring.scale}
        >
          <sphereGeometry args={[0.6, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="white"
            opacity={mainMenuSpring.opacity}
          />
        </animated.mesh>
        <animated.mesh position={[-5.5, 6, -2]} scale={printerViewSpring.scale}>
          <sphereGeometry args={[0.65, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="black"
            opacity={mainMenuSpring.opacity}
            side={BackSide}
          />
        </animated.mesh>
        <Billboard position={[-5.5, 7.5, -2]}>
          <AnimatedText
            font="/assets/inter.ttf"
            fontSize={0.75}
            outlineWidth={0.05}
            fillOpacity={mainMenuSpring.opacity}
            outlineOpacity={mainMenuSpring.opacity}
          >
            Hobby Corner
          </AnimatedText>
        </Billboard>
        <animated.mesh
          ref={sideRef}
          position={[2.525, 6, 6.75]}
          onClick={
            currentView === View.MainView
              ? () => goToView(View.ResumeView)
              : undefined
          }
          onPointerOver={
            currentView === View.MainView
              ? () => setHovered(View.ResumeView)
              : undefined
          }
          onPointerOut={() => setHovered(null)}
          scale={resumeViewSpring.scale}
        >
          <sphereGeometry args={[0.6, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="white"
            opacity={mainMenuSpring.opacity}
          />
        </animated.mesh>
        <animated.mesh
          position={[2.525, 6, 6.75]}
          scale={resumeViewSpring.scale}
        >
          <sphereGeometry args={[0.65, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="black"
            opacity={mainMenuSpring.opacity}
            side={BackSide}
          />
        </animated.mesh>
        <Billboard position={[2.525, 7.5, 6.75]}>
          <AnimatedText
            font="/assets/inter.ttf"
            fontSize={0.75}
            outlineWidth={0.05}
            fillOpacity={mainMenuSpring.opacity}
            outlineOpacity={mainMenuSpring.opacity}
          >
            Resume
          </AnimatedText>
        </Billboard>
      </group>
      {/* Desktop Menu */}
      <group>
        <animated.mesh
          ref={cellphoneRef}
          position={[2.358, 5.125, -2.055]}
          onClick={
            currentView === View.DesktopView
              ? () => goToView(View.CellphoneView)
              : undefined
          }
          onPointerOver={
            currentView === View.DesktopView
              ? () => setHovered(View.CellphoneView)
              : undefined
          }
          onPointerOut={() => setHovered(null)}
          scale={cellphoneViewSpring.scale}
        >
          <sphereGeometry args={[0.25, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="white"
            opacity={desktopMenuSpring.opacity}
          />
        </animated.mesh>
        <animated.mesh
          position={[2.358, 5.125, -2.055]}
          scale={cellphoneViewSpring.scale}
        >
          <sphereGeometry args={[0.25 + 1 / 48, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="black"
            opacity={desktopMenuSpring.opacity}
            side={BackSide}
          />
        </animated.mesh>
        <Billboard position={[2.358, 5.75, -2.055]}>
          <AnimatedText
            font="/assets/inter.ttf"
            fontSize={0.3125}
            outlineWidth={1 / 48}
            fillOpacity={desktopMenuSpring.opacity}
            outlineOpacity={desktopMenuSpring.opacity}
          >
            Socials
          </AnimatedText>
        </Billboard>
        <animated.mesh
          ref={pcRef}
          position={[3.25, 5.982, 1.668]}
          onClick={
            currentView === View.DesktopView
              ? () => goToView(View.PCView)
              : undefined
          }
          onPointerOver={
            currentView === View.DesktopView
              ? () => setHovered(View.PCView)
              : undefined
          }
          onPointerOut={() => setHovered(null)}
          scale={pcViewSpring.scale}
        >
          <sphereGeometry args={[0.25, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="white"
            opacity={desktopMenuSpring.opacity}
          />
        </animated.mesh>
        <animated.mesh
          position={[3.25, 5.982, 1.668]}
          scale={pcViewSpring.scale}
        >
          <sphereGeometry args={[0.25 + 1 / 48, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="black"
            opacity={desktopMenuSpring.opacity}
            side={BackSide}
          />
        </animated.mesh>
        <Billboard position={[3.25, 6.607, 1.668]}>
          <AnimatedText
            font="/assets/inter.ttf"
            fontSize={0.3125}
            outlineWidth={1 / 48}
            fillOpacity={desktopMenuSpring.opacity}
            outlineOpacity={desktopMenuSpring.opacity}
          >
            Projects
          </AnimatedText>
        </Billboard>
        <animated.mesh
          ref={pcRef}
          position={[3.25, 5.982, -2.15]}
          onClick={
            currentView === View.DesktopView
              ? () => goToView(View.CreditsView)
              : undefined
          }
          onPointerOver={
            currentView === View.DesktopView
              ? () => setHovered(View.CreditsView)
              : undefined
          }
          onPointerOut={() => setHovered(null)}
          scale={creditsViewSpring.scale}
        >
          <sphereGeometry args={[0.25, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="white"
            opacity={creditsButtonSpring.opacity}
          />
        </animated.mesh>
        <animated.mesh
          position={[3.25, 5.982, -2.15]}
          scale={creditsViewSpring.scale}
        >
          <sphereGeometry args={[0.25 + 1 / 48, 32, 32]} />
          <animated.meshStandardMaterial
            transparent
            color="black"
            opacity={creditsButtonSpring.opacity}
            side={BackSide}
          />
        </animated.mesh>
      </group>
    </>
  );
};
