"use client";

import {
  ContactShadows,
  Environment,
  MeshTransmissionMaterial,
  RoundedBox,
} from "@react-three/drei";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import {
  MutableRefObject,
  Suspense,
  useMemo,
  useRef,
} from "react";
import * as THREE from "three";

type SceneProps = {
  progressRef: MutableRefObject<number>;
  pointerRef: MutableRefObject<{ x: number; y: number }>;
  onReady: () => void;
};

function clamp(value: number) {
  return Math.min(1, Math.max(0, value));
}

function smoothstep(start: number, end: number, value: number) {
  const t = clamp((value - start) / (end - start));
  return t * t * (3 - 2 * t);
}

function damp(
  current: number,
  target: number,
  speed: number,
  delta: number,
) {
  return THREE.MathUtils.damp(current, target, speed, delta);
}

function CleaningCore() {
  const innerDisc = useRef<THREE.Group>(null);
  const waterLens = useRef<THREE.Mesh>(null);
  const lightRing = useRef<THREE.MeshBasicMaterial>(null);

  useFrame((state, delta) => {
    if (innerDisc.current) {
      innerDisc.current.rotation.z -= delta * 0.14;
    }

    if (waterLens.current) {
      waterLens.current.rotation.z =
        Math.sin(state.clock.elapsedTime * 0.32) * 0.015;
    }

    if (lightRing.current) {
      lightRing.current.opacity =
        0.68 +
        Math.sin(state.clock.elapsedTime * 1.1) * 0.1;
    }
  });

  return (
    <group>
      {/* Rear ceramic body */}
      <mesh castShadow receiveShadow>
        <cylinderGeometry args={[2.3, 2.3, 0.7, 96]} />
        <meshPhysicalMaterial
          color="#f8fbfc"
          roughness={0.12}
          metalness={0.08}
          clearcoat={1}
          clearcoatRoughness={0.04}
          envMapIntensity={1.6}
        />
      </mesh>

      {/* Aluminium edge */}
      <mesh position={[0, 0, 0.37]}>
        <torusGeometry args={[1.98, 0.23, 32, 128]} />
        <meshPhysicalMaterial
          color="#cbd5d9"
          roughness={0.15}
          metalness={0.88}
          clearcoat={0.8}
          envMapIntensity={2}
        />
      </mesh>

      {/* Soft ceramic front */}
      <mesh position={[0, 0, 0.48]}>
        <cylinderGeometry args={[1.76, 1.76, 0.26, 96]} />
        <meshPhysicalMaterial
          color="#ffffff"
          roughness={0.09}
          metalness={0.04}
          clearcoat={1}
          clearcoatRoughness={0.025}
          envMapIntensity={1.5}
        />
      </mesh>

      {/* Transparent blue cleaning chamber */}
      <mesh ref={waterLens} position={[0, 0, 0.69]}>
        <cylinderGeometry args={[1.43, 1.43, 0.25, 96]} />
        <MeshTransmissionMaterial
          color="#51c9ee"
          transmission={1}
          thickness={1.2}
          roughness={0.025}
          ior={1.34}
          chromaticAberration={0.015}
          anisotropy={0.12}
          distortion={0.025}
          distortionScale={0.12}
          temporalDistortion={0.015}
          samples={6}
          resolution={512}
          backside
        />
      </mesh>

      {/* Liquid depth */}
      <mesh position={[0, -0.16, 0.81]}>
        <circleGeometry args={[1.21, 96]} />
        <meshPhysicalMaterial
          color="#058bc8"
          transparent
          opacity={0.46}
          roughness={0.04}
          transmission={0.18}
          emissive="#047ab0"
          emissiveIntensity={0.08}
        />
      </mesh>

      {/* Inner rotating cleaning plate */}
      <group ref={innerDisc} position={[0, 0, 0.9]}>
        <mesh>
          <circleGeometry args={[0.82, 96]} />
          <meshPhysicalMaterial
            color="#eafaff"
            roughness={0.15}
            metalness={0.16}
            clearcoat={0.85}
          />
        </mesh>

        {[0, 1, 2, 3, 4, 5].map((index) => {
          const angle = (index / 6) * Math.PI * 2;

          return (
            <RoundedBox
              key={index}
              args={[0.56, 0.13, 0.08]}
              radius={0.06}
              smoothness={5}
              position={[
                Math.cos(angle) * 0.46,
                Math.sin(angle) * 0.46,
                0.06,
              ]}
              rotation={[0, 0, angle]}
            >
              <meshPhysicalMaterial
                color={index % 2 === 0 ? "#169fd2" : "#d9f7ff"}
                roughness={0.28}
                metalness={0.1}
              />
            </RoundedBox>
          );
        })}
      </group>

      {/* Illuminated ring */}
      <mesh position={[0, 0, 0.99]}>
        <ringGeometry args={[0.96, 1.02, 128]} />
        <meshBasicMaterial
          ref={lightRing}
          color="#55ddff"
          transparent
          opacity={0.72}
          toneMapped={false}
        />
      </mesh>

      {/* Minimal center mark */}
      <mesh position={[0, 0, 1.03]} rotation={[0, 0, Math.PI / 4]}>
        <boxGeometry args={[0.27, 0.27, 0.045]} />
        <meshPhysicalMaterial
          color="#087fbd"
          roughness={0.16}
          metalness={0.35}
          clearcoat={0.8}
        />
      </mesh>

      {/* Small side control */}
      <RoundedBox
        args={[0.78, 0.22, 0.18]}
        radius={0.09}
        smoothness={6}
        position={[1.72, -0.38, 0.34]}
        rotation={[0, 0.14, -0.12]}
      >
        <meshPhysicalMaterial
          color="#152f3b"
          roughness={0.16}
          metalness={0.62}
          clearcoat={0.7}
        />
      </RoundedBox>

      <mesh position={[1.72, -0.38, 0.445]}>
        <planeGeometry args={[0.4, 0.035]} />
        <meshBasicMaterial color="#5be0ff" toneMapped={false} />
      </mesh>
    </group>
  );
}

function AppleProductStory({
  progressRef,
  pointerRef,
  onReady,
}: SceneProps) {
  const root = useRef<THREE.Group>(null);
  const product = useRef<THREE.Group>(null);
  const readySent = useRef(false);

  const { camera, size } = useThree();

  const isMobile = size.width < 768;
  const isTablet = size.width >= 768 && size.width < 1100;

  const chapters = useMemo(
    () => [
      {
        start: 0,
        end: 0.16,
        x: 0,
        y: isMobile ? -1.15 : -0.55,
        z: isMobile ? -0.2 : 0,
        scale: isMobile ? 0.74 : isTablet ? 0.9 : 1,
        rx: -0.95,
        ry: 0,
        rz: 0,
      },
      {
        start: 0.16,
        end: 0.34,
        x: isMobile ? 0.05 : 1.65,
        y: isMobile ? -0.7 : -0.12,
        z: 1.35,
        scale: isMobile ? 0.93 : isTablet ? 1.15 : 1.38,
        rx: -0.7,
        ry: 0.22,
        rz: -0.08,
      },
      {
        start: 0.34,
        end: 0.52,
        x: isMobile ? -0.12 : -1.92,
        y: isMobile ? -0.56 : 0.18,
        z: 2.05,
        scale: isMobile ? 1.08 : isTablet ? 1.35 : 1.62,
        rx: -0.5,
        ry: -0.3,
        rz: 0.08,
      },
      {
        start: 0.52,
        end: 0.7,
        x: isMobile ? 0.1 : 1.88,
        y: isMobile ? -0.88 : -0.1,
        z: 2.42,
        scale: isMobile ? 1.2 : isTablet ? 1.48 : 1.78,
        rx: -0.28,
        ry: 0.38,
        rz: -0.11,
      },
      {
        start: 0.7,
        end: 0.86,
        x: isMobile ? -0.08 : -1.2,
        y: isMobile ? -0.66 : 0.08,
        z: 1.15,
        scale: isMobile ? 0.94 : isTablet ? 1.15 : 1.36,
        rx: -0.62,
        ry: -0.2,
        rz: 0.04,
      },
      {
        start: 0.86,
        end: 1,
        x: 0,
        y: isMobile ? -1.02 : -0.5,
        z: 0.1,
        scale: isMobile ? 0.76 : isTablet ? 0.92 : 1.04,
        rx: -0.88,
        ry: 0.02,
        rz: 0,
      },
    ],
    [isMobile, isTablet],
  );

  useFrame((_, delta) => {
    const p = progressRef.current;
    const pointer = pointerRef.current;

    if (!readySent.current) {
      readySent.current = true;
      onReady();
    }

    let current = chapters[0];
    let next = chapters[1];
    let t = 0;

    for (let index = 0; index < chapters.length; index += 1) {
      const chapter = chapters[index];

      if (p >= chapter.start && p <= chapter.end) {
        current = chapter;
        next = chapters[Math.min(index + 1, chapters.length - 1)];
        t = smoothstep(chapter.start, chapter.end, p);
        break;
      }
    }

    const targetX = THREE.MathUtils.lerp(current.x, next.x, t);
    const targetY = THREE.MathUtils.lerp(current.y, next.y, t);
    const targetZ = THREE.MathUtils.lerp(current.z, next.z, t);
    const targetScale = THREE.MathUtils.lerp(
      current.scale,
      next.scale,
      t,
    );

    const targetRX = THREE.MathUtils.lerp(current.rx, next.rx, t);
    const targetRY = THREE.MathUtils.lerp(current.ry, next.ry, t);
    const targetRZ = THREE.MathUtils.lerp(current.rz, next.rz, t);

    if (root.current) {
      root.current.position.x = damp(
        root.current.position.x,
        targetX + pointer.x * (isMobile ? 0.015 : 0.045),
        3.7,
        delta,
      );

      root.current.position.y = damp(
        root.current.position.y,
        targetY - pointer.y * (isMobile ? 0.01 : 0.028),
        3.7,
        delta,
      );

      root.current.position.z = damp(
        root.current.position.z,
        targetZ,
        3.7,
        delta,
      );

      root.current.scale.setScalar(
        damp(root.current.scale.x, targetScale, 3.8, delta),
      );
    }

    if (product.current) {
      product.current.rotation.x = damp(
        product.current.rotation.x,
        targetRX,
        3.8,
        delta,
      );

      product.current.rotation.y = damp(
        product.current.rotation.y,
        targetRY + pointer.x * (isMobile ? 0.008 : 0.025),
        3.8,
        delta,
      );

      product.current.rotation.z = damp(
        product.current.rotation.z,
        targetRZ,
        3.8,
        delta,
      );
    }

    const cameraZ =
      (isMobile ? 11.8 : isTablet ? 10.4 : 9.4) -
      Math.sin(p * Math.PI) * (isMobile ? 0.35 : 0.65);

    camera.position.x = damp(camera.position.x, 0, 3.2, delta);
    camera.position.y = damp(
      camera.position.y,
      isMobile ? 0.48 : 0.14,
      3.2,
      delta,
    );
    camera.position.z = damp(camera.position.z, cameraZ, 3.2, delta);

    if (camera instanceof THREE.PerspectiveCamera) {
      const targetFov = isMobile ? 39 : isTablet ? 36 : 32;
      camera.fov = damp(camera.fov, targetFov, 3.2, delta);
      camera.updateProjectionMatrix();
    }

    camera.lookAt(0, isMobile ? -0.45 : -0.04, 0);
  });

  return (
    <>
      <ambientLight intensity={1.05} />

      <directionalLight
        position={[5, 7, 7]}
        intensity={4.2}
        color="#ffffff"
        castShadow
        shadow-mapSize-width={1024}
        shadow-mapSize-height={1024}
      />

      <directionalLight
        position={[-5, 1, 4]}
        intensity={1.85}
        color="#78d9f3"
      />

      <pointLight
        position={[0, -2, 4]}
        intensity={7}
        distance={11}
        color="#3ec8ed"
      />

      <pointLight
        position={[3.5, 2.5, 2]}
        intensity={4}
        distance={8}
        color="#ffffff"
      />

      <group ref={root}>
        <group ref={product}>
          <CleaningCore />
        </group>
      </group>

      <ContactShadows
        position={[0, -2.7, 0]}
        scale={8}
        opacity={0.2}
        blur={4.3}
        far={5}
      />

      <Environment preset="studio" />
    </>
  );
}

export default function LarkShine3DScene(props: SceneProps) {
  return (
    <Canvas
      dpr={[1, 1.7]}
      camera={{
        position: [0, 0.15, 9.4],
        fov: 32,
        near: 0.1,
        far: 100,
      }}
      gl={{
        antialias: true,
        alpha: true,
        powerPreference: "high-performance",
        toneMapping: THREE.ACESFilmicToneMapping,
        toneMappingExposure: 1.08,
      }}
      shadows
    >
      <Suspense fallback={null}>
        <AppleProductStory {...props} />
      </Suspense>
    </Canvas>
  );
}
