import { useCubesContext } from "@/contexts/cubes-context";
import { FacingDirection } from "./consts";
import { RotationPanel } from "./rotation-panel";
import { Group } from "three";
import { Fragment, useRef } from "react";
import { useFrame } from "@react-three/fiber";

type RotateArgs = {
  rotatingFaceDirection: FacingDirection;
  rotatingDirection: "clockwise" | "counter-clockwise";
  rotatingGroup: Group;
};

type RotatorProps = {
  cubeSpeed: number;
};

export const Rotator = ({ cubeSpeed }: RotatorProps) => {
  const { getCubes, cubeGroupRef } = useCubesContext();
  const isRotating = useRef(false);
  const rotateArgs = useRef<RotateArgs>({
    rotatingFaceDirection: "front",
    rotatingDirection: "clockwise",
    rotatingGroup: new Group(),
  });

  useFrame((state, delta) => {
    const { rotatingFaceDirection, rotatingDirection, rotatingGroup } =
      rotateArgs.current;
    const cubeGroup = cubeGroupRef.current;
    if (!isRotating.current || !cubeGroup) return;

    let sign = 0;
    switch (rotatingFaceDirection) {
      case "front":
        sign = rotatingDirection === "clockwise" ? -1 : 1;
        rotatingGroup.rotation.z += sign * delta * cubeSpeed;
        if (Math.abs(rotatingGroup.rotation.z) > Math.PI / 2) {
          rotatingGroup.rotation.z = (Math.PI / 2) * sign;
          isRotating.current = false;
        }
        break;
      case "back":
        sign = rotatingDirection === "clockwise" ? 1 : -1;
        rotatingGroup.rotation.z += sign * delta * cubeSpeed;
        if (Math.abs(rotatingGroup.rotation.z) > Math.PI / 2) {
          rotatingGroup.rotation.z = (Math.PI / 2) * sign;
          isRotating.current = false;
        }
        break;
      case "left":
        sign = rotatingDirection === "clockwise" ? 1 : -1;
        rotatingGroup.rotation.x += sign * delta * cubeSpeed;
        if (Math.abs(rotatingGroup.rotation.x) > Math.PI / 2) {
          rotatingGroup.rotation.x = (Math.PI / 2) * sign;
          isRotating.current = false;
        }
        break;
      case "right":
        sign = rotatingDirection === "clockwise" ? -1 : 1;
        rotatingGroup.rotation.x += sign * delta * cubeSpeed;
        if (Math.abs(rotatingGroup.rotation.x) > Math.PI / 2) {
          rotatingGroup.rotation.x = (Math.PI / 2) * sign;
          isRotating.current = false;
        }
        break;
      case "top":
        sign = rotatingDirection === "clockwise" ? -1 : 1;
        rotatingGroup.rotation.y += sign * delta * cubeSpeed;
        if (Math.abs(rotatingGroup.rotation.y) > Math.PI / 2) {
          rotatingGroup.rotation.y = (Math.PI / 2) * sign;
          isRotating.current = false;
        }
        break;
      case "bottom":
        sign = rotatingDirection === "clockwise" ? 1 : -1;
        rotatingGroup.rotation.y += sign * delta * cubeSpeed;
        if (Math.abs(rotatingGroup.rotation.y) > Math.PI / 2) {
          rotatingGroup.rotation.y = (Math.PI / 2) * sign;
          isRotating.current = false;
        }
        break;
    }

    if (isRotating.current) return;
    const children = [...rotatingGroup.children];
    children.forEach((child) => cubeGroup.attach(child));
    cubeGroup.remove(rotatingGroup);
  });

  const handleClick = (
    facingDirection: FacingDirection,
    direction: "clockwise" | "counter-clockwise",
  ) => {
    if (isRotating.current || !cubeGroupRef.current) return;
    const cubes = getCubes(facingDirection);

    rotateArgs.current.rotatingFaceDirection = facingDirection;
    rotateArgs.current.rotatingDirection = direction;
    rotateArgs.current.rotatingGroup = new Group();
    cubeGroupRef.current.add(rotateArgs.current.rotatingGroup);
    cubes.forEach((cube) => rotateArgs.current.rotatingGroup.attach(cube));

    isRotating.current = true;
  };

  return (
    <>
      {["front", "back", "left", "right", "top", "bottom"].map(
        (facingDirection) => (
          <Fragment key={facingDirection}>
            <RotationPanel
              direction="clockwise"
              facingDirection={facingDirection as FacingDirection}
              onClick={handleClick}
            />
            <RotationPanel
              direction="counter-clockwise"
              facingDirection={facingDirection as FacingDirection}
              onClick={handleClick}
            />
          </Fragment>
        ),
      )}
    </>
  );
};
