import { useEffect, useRef, useState, memo } from "react";
import { motion } from "framer-motion";
import * as THREE from "three";
import { useConfig } from "../../context/LauncherContext";
type UVSet = Record<string, number[]>;
interface SkinViewerProps {
  username: string;
  setUsername: (name: string) => void;
  playPressSound: () => void;
  skinUrl: string;
  capeUrl?: string | null;
  setSkinUrl: (url: string) => void;
  setActiveView: (view: string) => void;
  isFocusedSection: boolean;
  onNavigateRight: () => void;
  hideControls?: boolean;
  style?: React.CSSProperties;
}

const SkinViewer = memo(function SkinViewer({
  username,
  setUsername,
  playPressSound,
  skinUrl,
  capeUrl,
  setActiveView,
  isFocusedSection,
  onNavigateRight,
  hideControls,
  style,
}: SkinViewerProps) {
  const mountRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [focusIndex, setFocusIndex] = useState(0);
  const { legacyMode } = useConfig();
  const overlaysRef = useRef<THREE.Mesh[]>([]);
  const capeRef = useRef<THREE.Group | null>(null);
  const capeOrigRef = useRef<{ y: number; rx: number; meshY: number } | null>(
    null,
  );
  const playerGroupRef = useRef<THREE.Group | null>(null);
  const easterEggRef = useRef<THREE.Group | null>(null);
  const requestRenderRef = useRef<(() => void) | null>(null);
  useEffect(() => {
    if (!mountRef.current) return;
    const width = 260;
    const height = 450;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(35, width / height, 0.1, 1000);
    camera.position.set(0, 0, 68);
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(window.devicePixelRatio);
    mountRef.current.innerHTML = "";
    mountRef.current.appendChild(renderer.domElement);
    scene.add(new THREE.AmbientLight(0xffffff, 0.4));
    scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 0.6));
    const dl = new THREE.DirectionalLight(0xffffff, 0.8);
    dl.position.set(10, 20, 10);
    scene.add(dl);
    const playerGroup = new THREE.Group();
    playerGroup.position.y = -1.5;
    scene.add(playerGroup);
    playerGroupRef.current = playerGroup;
    const textureLoader = new THREE.TextureLoader();
    textureLoader.load(skinUrl || "/images/Default.png", (texture) => {
      texture.magFilter = THREE.NearestFilter;
      texture.minFilter = THREE.NearestFilter;
      texture.colorSpace = THREE.SRGBColorSpace;
      const img = texture.image;
      const isLegacy = img.height === 32;
      const createFaceMaterial = (
        x: number,
        y: number,
        w: number,
        h: number,
        flipX = false,
        flipY = false,
        tex = texture,
      ) => {
        const matTex = tex.clone();
        matTex.repeat.set(
          (flipX ? -w : w) / 64,
          (flipY ? -h : h) / tex.image.height,
        );
        matTex.offset.set(
          (flipX ? x + w : x) / 64,
          1 - (flipY ? y : y + h) / tex.image.height,
        );
        matTex.needsUpdate = true;
        return new THREE.MeshLambertMaterial({
          map: matTex,
          transparent: true,
          alphaTest: 0.5,
          side: THREE.FrontSide,
        });
      };

      const createPart = (
        w: number,
        h: number,
        d: number,
        uv: UVSet,
        overlayUv?: UVSet,
        swapMats = false,
        isLegacyMirror = false,
      ) => {
        const group = new THREE.Group();
        const geo = new THREE.BoxGeometry(w, h, d);
        const getMats = (uvSet: UVSet) => {
          const flipX = isLegacyMirror;
          return [
            createFaceMaterial(
              swapMats ? uvSet.right[0] : uvSet.left[0],
              uvSet.left[1],
              uvSet.left[2],
              uvSet.left[3],
              flipX,
            ),
            createFaceMaterial(
              swapMats ? uvSet.left[0] : uvSet.right[0],
              uvSet.right[1],
              uvSet.right[2],
              uvSet.right[3],
              flipX,
            ),
            createFaceMaterial(
              uvSet.top[0],
              uvSet.top[1],
              uvSet.top[2],
              uvSet.top[3],
              flipX,
              true,
            ),
            createFaceMaterial(
              uvSet.bottom[0],
              uvSet.bottom[1],
              uvSet.bottom[2],
              uvSet.bottom[3],
              flipX,
              true,
            ),
            createFaceMaterial(
              uvSet.front[0],
              uvSet.front[1],
              uvSet.front[2],
              uvSet.front[3],
              flipX,
            ),
            createFaceMaterial(
              uvSet.back[0],
              uvSet.back[1],
              uvSet.back[2],
              uvSet.back[3],
              !flipX,
            ),
          ];
        };

        const mesh = new THREE.Mesh(geo, getMats(uv));
        group.add(mesh);
        if (overlayUv) {
          const oGeo = new THREE.BoxGeometry(w + 0.5, h + 0.5, d + 0.5);
          const oMesh = new THREE.Mesh(oGeo, getMats(overlayUv));
          oMesh.visible = true;
          overlaysRef.current.push(oMesh);
          group.add(oMesh);
        }
        return group;
      };

      const limbUv = (x: number, y: number, w = 4) => ({
        top: [x + 4, y, w, 4],
        bottom: [x + 4 + w, y, w, 4],
        right: [x, y + 4, 4, 12],
        front: [x + 4, y + 4, w, 12],
        left: [x + 4 + w, y + 4, 4, 12],
        back: [x + 8 + w, y + 4, w, 12],
      });

      const isSlim =
        !isLegacy &&
        (() => {
          const canvas = document.createElement("canvas");
          canvas.width = img.width;
          canvas.height = img.height;
          const ctx = canvas.getContext("2d");
          if (!ctx) return false;
          ctx.drawImage(img, 0, 0);
          const data = ctx.getImageData(42, 48, 1, 1).data;
          return data[3] === 0;
        })();
      const armW = isSlim ? 3 : 4;
      const headUv = {
        top: [8, 0, 8, 8],
        bottom: [16, 0, 8, 8],
        right: [0, 8, 8, 8],
        left: [16, 8, 8, 8],
        front: [8, 8, 8, 8],
        back: [24, 8, 8, 8],
      };
      const hatUv = {
        top: [40, 0, 8, 8],
        bottom: [48, 0, 8, 8],
        right: [32, 8, 8, 8],
        left: [48, 8, 8, 8],
        front: [40, 8, 8, 8],
        back: [56, 8, 8, 8],
      };
      const head = createPart(8, 8, 8, headUv, hatUv);
      head.position.y = 10;
      playerGroup.add(head);
      const easterEggGroup = new THREE.Group();
      const earMat = new THREE.MeshLambertMaterial({ color: 0xffb6c1 });
      const earGeo = new THREE.BoxGeometry(3.5, 3.5, 2);
      const leftEar = new THREE.Mesh(earGeo, earMat);
      leftEar.position.set(-5, 16, 0);
      leftEar.renderOrder = 1;
      leftEar.visible = false;
      easterEggGroup.add(leftEar);
      const rightEar = new THREE.Mesh(earGeo, earMat);
      rightEar.position.set(5, 16, 0);
      rightEar.renderOrder = 1;
      rightEar.visible = false;
      easterEggGroup.add(rightEar);
      playerGroup.add(easterEggGroup);
      easterEggRef.current = easterEggGroup;
      {
        const n = username.toLowerCase();
        if (n === "deadmau5") {
          easterEggGroup.children.forEach((child, i) => {
            if (i < 2) child.visible = true;
          });
        }
      }
      const bodyUv = {
        top: [20, 16, 8, 4],
        bottom: [28, 16, 8, 4],
        right: [16, 20, 4, 12],
        left: [28, 20, 4, 12],
        front: [20, 20, 8, 12],
        back: [32, 20, 8, 12],
      };
      const jacketUv = {
        top: [20, 32, 8, 4],
        bottom: [28, 32, 8, 4],
        right: [16, 36, 4, 12],
        left: [28, 36, 4, 12],
        front: [20, 36, 8, 12],
        back: [32, 36, 8, 12],
      };
      playerGroup.add(
        createPart(8, 12, 4, bodyUv, isLegacy ? undefined : jacketUv),
      );
      const rightArm = createPart(
        armW,
        12,
        4,
        limbUv(40, 16, armW),
        isLegacy ? undefined : limbUv(40, 32, armW),
      );
      rightArm.position.set(isSlim ? -5.5 : -6, 0, 0);
      playerGroup.add(rightArm);
      const leftArm = createPart(
        armW,
        12,
        4,
        isLegacy ? limbUv(40, 16, armW) : limbUv(32, 48, armW),
        isLegacy ? undefined : limbUv(48, 48, armW),
        true,
        isLegacy,
      );
      leftArm.position.set(isSlim ? 5.5 : 6, 0, 0);
      playerGroup.add(leftArm);
      const rightLeg = createPart(
        4,
        12,
        4,
        limbUv(0, 16),
        isLegacy ? undefined : limbUv(0, 32),
      );
      rightLeg.position.set(-2, -12, 0);
      playerGroup.add(rightLeg);
      const leftLeg = createPart(
        4,
        12,
        4,
        isLegacy ? limbUv(0, 16) : limbUv(16, 48),
        isLegacy ? undefined : limbUv(0, 48),
        true,
        isLegacy,
      );
      leftLeg.position.set(2, -12, 0);
      playerGroup.add(leftLeg);
      if (capeUrl) {
        textureLoader.load(capeUrl, (capeTexture) => {
          capeTexture.magFilter = THREE.NearestFilter;
          capeTexture.minFilter = THREE.NearestFilter;
          capeTexture.colorSpace = THREE.SRGBColorSpace;
          const capeUv = {
            top: [1, 0, 10, 1],
            bottom: [11, 0, 10, 1],
            right: [0, 1, 1, 16],
            front: [1, 1, 10, 16],
            left: [11, 1, 1, 16],
            back: [12, 1, 10, 16],
          };

          const capeGroup = new THREE.Group();
          const capeGeo = new THREE.BoxGeometry(10, 16, 1);
          const capeMats = [
            createFaceMaterial(
              capeUv.left[0],
              capeUv.left[1],
              capeUv.left[2],
              capeUv.left[3],
              false,
              false,
              capeTexture,
            ),
            createFaceMaterial(
              capeUv.right[0],
              capeUv.right[1],
              capeUv.right[2],
              capeUv.right[3],
              false,
              false,
              capeTexture,
            ),
            createFaceMaterial(
              capeUv.top[0],
              capeUv.top[1],
              capeUv.top[2],
              capeUv.top[3],
              false,
              true,
              capeTexture,
            ),
            createFaceMaterial(
              capeUv.bottom[0],
              capeUv.bottom[1],
              capeUv.bottom[2],
              capeUv.bottom[3],
              false,
              true,
              capeTexture,
            ),
            createFaceMaterial(
              capeUv.back[0],
              capeUv.back[1],
              capeUv.back[2],
              capeUv.back[3],
              false,
              false,
              capeTexture,
            ),
            createFaceMaterial(
              capeUv.front[0],
              capeUv.front[1],
              capeUv.front[2],
              capeUv.front[3],
              false,
              false,
              capeTexture,
            ),
          ];

          const capeMesh = new THREE.Mesh(capeGeo, capeMats);
          capeMesh.position.set(0, -8, -0.5);
          capeGroup.add(capeMesh);
          capeGroup.position.set(0, 6, -2.35);
          capeGroup.rotation.x = 0.15;
          playerGroup.add(capeGroup);
          capeRef.current = capeGroup;
          capeOrigRef.current = { y: 6, rx: 0.15, meshY: -8 };
        });
      }

      const name = username.toLowerCase();
      playerGroup.rotation.y = -0.3;
      if (name === "dinnerbone" || name === "grumm") {
        playerGroup.scale.y = -1;
        playerGroup.position.y = 1.5;
      }
      requestRenderRef.current?.();
    });

    let isDragging = false;
    let previousMousePosition = { x: 0, y: 0 };
    const onMouseDown = (e: MouseEvent) => {
      isDragging = true;
      previousMousePosition = { x: e.clientX, y: e.clientY };
    };
    const onMouseUp = () => {
      isDragging = false;
    };
    const onMouseMove = (e: MouseEvent) => {
      if (isDragging) {
        const delta = (e.clientX - previousMousePosition.x) * 0.01;
        playerGroup.rotation.y += delta;
        previousMousePosition = { x: e.clientX, y: e.clientY };
        requestRenderRef.current?.();
      }
    };

    requestRenderRef.current = () => renderer.render(scene, camera);
    requestRenderRef.current();
    renderer.domElement.addEventListener("mousedown", onMouseDown);
    window.addEventListener("mousemove", onMouseMove);
    window.addEventListener("mouseup", onMouseUp);
    return () => {
      window.removeEventListener("mousemove", onMouseMove);
      window.removeEventListener("mouseup", onMouseUp);
      scene.traverse((object) => {
        if (object instanceof THREE.Mesh) {
          if (object.geometry) object.geometry.dispose();
          if (object.material) {
            if (Array.isArray(object.material)) {
              object.material.forEach((mat) => {
                if (mat.map) mat.map.dispose();
                mat.dispose();
              });
            } else {
              if (object.material.map) object.material.map.dispose();
              object.material.dispose();
            }
          }
        }
      });
      renderer.dispose();
      overlaysRef.current = [];
      capeRef.current = null;
      capeOrigRef.current = null;
      playerGroupRef.current = null;
      easterEggRef.current = null;
      requestRenderRef.current = null;
    };
  }, [skinUrl, capeUrl]);

  useEffect(() => {
    const group = playerGroupRef.current;
    if (!group) return;
    const name = username.toLowerCase();
    const flip = name === "dinnerbone" || name === "grumm";
    group.scale.y = flip ? -1 : 1;
    group.position.y = flip ? 1.5 : -1.5;
    const cape = capeRef.current;
    const orig = capeOrigRef.current;
    if (cape && orig) {
      cape.position.y = flip ? -orig.y : orig.y;
      cape.rotation.x = flip ? -orig.rx : orig.rx;
      cape.children.forEach((child) => {
        if (child instanceof THREE.Mesh) {
          child.position.y = flip ? -orig.meshY : orig.meshY;
        }
      });
    }
    const egg = easterEggRef.current;
    if (egg) {
      egg.children.forEach((child) => {
        if (child instanceof THREE.Mesh) {
          child.visible = false;
        }
      });
      if (name === "deadmau5") {
        egg.children.forEach((child, i) => {
          if (i < 2) child.visible = true;
        });
      }
    }
    requestRenderRef.current?.();
  }, [username]);

  useEffect(() => {
    if (!isFocusedSection) {
      setFocusIndex(legacyMode ? 1 : 0);
      return;
    }

    const handleKeyDown = (e: KeyboardEvent) => {
      if (document.activeElement?.tagName === "INPUT") return;
      if (e.key === "ArrowRight") {
        if (legacyMode) onNavigateRight();
        else if (focusIndex === 3) onNavigateRight();
        else if (focusIndex >= 1 && focusIndex < 3)
          setFocusIndex((prev) => prev + 1);
      } else if (e.key === "ArrowLeft") {
        if (legacyMode) return;
        if (focusIndex > 1 && focusIndex <= 3)
          setFocusIndex((prev) => prev - 1);
      } else if (e.key === "ArrowDown") {
        setFocusIndex((prev) => (prev < 3 ? prev + 1 : prev));
      } else if (e.key === "ArrowUp") {
        setFocusIndex((prev) => (prev > 0 ? prev - 1 : prev));
      } else if (e.key === "Enter") {
        if (focusIndex === 0) {
          (
            containerRef.current?.querySelector("input") as HTMLElement
          )?.focus();
        } else if (focusIndex === 1) {
          playPressSound();
          setActiveView("skins");
        } else if (focusIndex === 2) {
          playPressSound();
          setActiveView("screenshots");
        } else if (focusIndex === 3) {
          playPressSound();
          setActiveView("lcelive");
        }
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [
    isFocusedSection,
    focusIndex,
    onNavigateRight,
    playPressSound,
    setActiveView,
    legacyMode,
  ]);

  useEffect(() => {
    if (isFocusedSection) {
      const el = containerRef.current?.querySelector(
        `[data-focus="${focusIndex}"]`,
      ) as HTMLElement;
      if (el && document.activeElement?.tagName !== "INPUT") el.focus();
    }
  }, [isFocusedSection, focusIndex]);

  return (
    <motion.div
      ref={containerRef}
      initial={{ opacity: 0, x: -20 }}
      animate={{ opacity: 1, x: 0 }}
      exit={{ opacity: 0, x: -20 }}
      transition={{ duration: useConfig().animationsEnabled ? 0.3 : 0 }}
      className={`absolute ${legacyMode ? "left-[calc(50vw-340px)]" : "left-16"} ${legacyMode ? "top-1/2" : "top-[40%]"} -translate-y-1/2 flex flex-col items-center gap-1 outline-none z-10`}
      style={style}
    >
      {!hideControls && !legacyMode && (
        <div
          className={`relative z-20 bg-black/20 flex justify-center items-center ${legacyMode ? "mb-0" : "mb-2"} px-2 py-1 rounded-sm border-2 transition-colors ${isFocusedSection && focusIndex === 0 ? "border-[#FFFF55]" : "border-transparent"}`}
          data-focus="0"
          tabIndex={0}
        >
          <input
            type="text"
            value={username}
            maxLength={16}
            style={{ width: `${Math.max(username.length, 3) + 2}ch` }}
            onChange={(e) => setUsername(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.currentTarget.blur();
                e.stopPropagation();
              }
            }}
            className="bg-transparent text-white focus:text-[#FFFF55] outline-none border-none text-center font-['Mojangles'] mc-text-shadow tracking-widest text-xl cursor-text"
          />
        </div>
      )}
      {!legacyMode && (
        <div className="w-[220px] h-[380px] relative flex items-center justify-center">
          <div
            ref={mountRef}
            className="absolute drop-shadow-[0_8px_8px_rgba(0,0,0,0.8)] cursor-ew-resize outline-none w-[260px] h-[450px] -translate-y-6"
          />
        </div>
      )}
      {!hideControls && (
        <div
          className={`flex ${legacyMode ? "flex-col gap-2 mt-0" : "flex-row gap-4 mt-2"} items-center`}
        >
          <button
            data-focus="1"
            tabIndex={0}
            onMouseEnter={() => isFocusedSection && setFocusIndex(1)}
            onClick={() => {
              playPressSound();
              setActiveView("skins");
            }}
            className={`mc-sq-btn w-12 h-12 flex items-center justify-center outline-none border-none ${isFocusedSection && focusIndex === 1 ? "" : ""}`}
            style={
              isFocusedSection && focusIndex === 1
                ? {
                    backgroundImage:
                      "url('/images/Button_Square_Highlighted.png')",
                  }
                : {}
            }
            title="Change Skin"
          >
            <img
              src="/images/Change_Skin_Icon.png"
              alt="Skin"
              className="w-8 h-8 object-contain"
              style={{ imageRendering: "pixelated" }}
            />
          </button>
          <button
            data-focus="2"
            tabIndex={0}
            onMouseEnter={() => isFocusedSection && setFocusIndex(2)}
            onClick={() => {
              playPressSound();
              setActiveView("screenshots");
            }}
            className={`mc-sq-btn w-12 h-12 flex items-center justify-center outline-none border-none ${isFocusedSection && focusIndex === 2 ? "" : ""}`}
            style={
              isFocusedSection && focusIndex === 2
                ? {
                    backgroundImage:
                      "url('/images/Button_Square_Highlighted.png')",
                  }
                : {}
            }
            title="Screenshots"
          >
            <img
              src="/images/Screenshots_Icon.png"
              alt="Screenshots"
              className="w-8 h-8 object-contain"
              style={{ imageRendering: "pixelated" }}
            />
          </button>
          <button
            data-focus="3"
            tabIndex={0}
            onMouseEnter={() => isFocusedSection && setFocusIndex(3)}
            onClick={() => {
              playPressSound();
              setActiveView("lcelive");
            }}
            className={`mc-sq-btn w-12 h-12 flex items-center justify-center outline-none border-none ${isFocusedSection && focusIndex === 3 ? "" : ""}`}
            style={
              isFocusedSection && focusIndex === 3
                ? {
                    backgroundImage:
                      "url('/images/Button_Square_Highlighted.png')",
                  }
                : {}
            }
            title="LCELive"
          >
            <img
              src="/images/friends.png"
              alt="LCELive"
              className="w-8 h-8 object-contain"
              style={{ imageRendering: "pixelated" }}
            />
          </button>
        </div>
      )}
    </motion.div>
  );
});

export default SkinViewer;
