"use client";

import {
  ContactShadows,
  Environment,
  Float,
  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 ProductModel() {
  const brush = useRef<THREE.Group>(null);
  const water = useRef<THREE.Mesh>(null);
  const displayGlow = useRef<THREE.MeshBasicMaterial>(null);

  useFrame((state, delta) => {
    if (brush.current) {
      brush.current.rotation.y -= delta * 0.65;
    }

    if (water.current) {
      water.current.position.y =
        0.32 + Math.sin(state.clock.elapsedTime * 0.7) * 0.018;
    }

    if (displayGlow.current) {
      displayGlow.current.opacity =
        0.72 +
        Math.sin(state.clock.elapsedTime * 1.6) * 0.12;
    }
  });

  return (
    <group>
      <RoundedBox
        args={[2.72, 1.25, 1.72]}
        radius={0.32}
        smoothness={8}
        position={[0, -0.57, 0]}
        castShadow
      >
        <meshPhysicalMaterial
          color="#f9fdff"
          roughness={0.16}
          metalness={0.22}
          clearcoat={1}
          clearcoatRoughness={0.08}
          envMapIntensity={1.45}
        />
      </RoundedBox>

      <RoundedBox
        args={[2.38, 0.42, 1.5]}
        radius={0.18}
        smoothness={6}
        position={[0, -1.16, 0]}
        castShadow
      >
        <meshPhysicalMaterial
          color="#102c39"
          roughness={0.24}
          metalness={0.62}
          clearcoat={0.6}
        />
      </RoundedBox>

      <RoundedBox
        args={[1.64, 1.52, 1.17]}
        radius={0.34}
        smoothness={8}
        position={[0, 0.64, 0]}
        castShadow
      >
        <MeshTransmissionMaterial
          color="#9cecff"
          transmission={1}
          thickness={1.18}
          roughness={0.045}
          ior={1.38}
          chromaticAberration={0.018}
          distortion={0.025}
          distortionScale={0.14}
          backside
          samples={4}
          resolution={256}
        />
      </RoundedBox>

      <mesh ref={water} position={[0, 0.32, 0]}>
        <boxGeometry args={[1.37, 0.72, 0.94]} />
        <meshPhysicalMaterial
          color="#108fd1"
          transparent
          opacity={0.56}
          transmission={0.14}
          roughness={0.04}
          emissive="#087cb8"
          emissiveIntensity={0.1}
        />
      </mesh>

      <RoundedBox
        args={[1.86, 0.62, 1.22]}
        radius={0.25}
        smoothness={7}
        position={[0, 1.61, 0]}
        castShadow
      >
        <meshPhysicalMaterial
          color="#ffffff"
          roughness={0.14}
          metalness={0.26}
          clearcoat={1}
          clearcoatRoughness={0.05}
        />
      </RoundedBox>

      <RoundedBox
        args={[1.08, 0.29, 0.075]}
        radius={0.08}
        smoothness={6}
        position={[0, 1.62, 0.63]}
      >
        <meshPhysicalMaterial
          color="#061a23"
          roughness={0.03}
          metalness={0.68}
          clearcoat={1}
        />
      </RoundedBox>

      <mesh position={[0, 1.62, 0.675]}>
        <planeGeometry args={[0.58, 0.048]} />
        <meshBasicMaterial
          ref={displayGlow}
          color="#5de3ff"
          transparent
          opacity={0.8}
          toneMapped={false}
        />
      </mesh>

      <RoundedBox
        args={[1.3, 0.35, 0.08]}
        radius={0.11}
        smoothness={6}
        position={[0, -0.55, 0.88]}
      >
        <meshPhysicalMaterial
          color="#ecfaff"
          roughness={0.13}
          metalness={0.3}
          clearcoat={1}
        />
      </RoundedBox>

      <group position={[0, -0.55, 0.935]}>
        <mesh rotation={[0, 0, Math.PI / 4]}>
          <boxGeometry args={[0.24, 0.24, 0.035]} />
          <meshBasicMaterial color="#149bd8" toneMapped={false} />
        </mesh>

        <mesh position={[0.27, 0, 0]}>
          <boxGeometry args={[0.23, 0.055, 0.035]} />
          <meshBasicMaterial color="#ffffff" toneMapped={false} />
        </mesh>

        <mesh position={[0.49, 0, 0]}>
          <boxGeometry args={[0.12, 0.055, 0.035]} />
          <meshBasicMaterial color="#58d3f4" toneMapped={false} />
        </mesh>
      </group>

      {[-1, 1].map((side) => (
        <group key={side}>
          <mesh
            position={[side * 1.19, -0.92, 0.1]}
            rotation={[0, 0, Math.PI / 2]}
            castShadow
          >
            <cylinderGeometry args={[0.39, 0.39, 0.25, 36]} />
            <meshPhysicalMaterial
              color="#0b202a"
              roughness={0.28}
              metalness={0.38}
              clearcoat={0.5}
            />
          </mesh>

          <mesh
            position={[side * 1.32, -0.92, 0.1]}
            rotation={[0, 0, Math.PI / 2]}
          >
            <cylinderGeometry args={[0.17, 0.17, 0.026, 28]} />
            <meshPhysicalMaterial
              color="#50cdea"
              roughness={0.18}
              metalness={0.55}
            />
          </mesh>
        </group>
      ))}

      <RoundedBox
        args={[3.16, 0.29, 1.22]}
        radius={0.16}
        smoothness={7}
        position={[0, -1.48, 0.42]}
        castShadow
      >
        <meshPhysicalMaterial
          color="#f2fbfe"
          roughness={0.17}
          metalness={0.2}
          clearcoat={0.9}
        />
      </RoundedBox>

      <group ref={brush} position={[0, -1.65, 0.42]}>
        <mesh rotation={[Math.PI / 2, 0, 0]}>
          <cylinderGeometry args={[0.5, 0.5, 0.085, 40]} />
          <meshPhysicalMaterial
            color="#159bd8"
            roughness={0.34}
            metalness={0.12}
          />
        </mesh>

        {[0, 1, 2, 3, 4, 5].map((index) => (
          <mesh
            key={index}
            position={[
              Math.cos((index / 6) * Math.PI * 2) * 0.3,
              -0.052,
              Math.sin((index / 6) * Math.PI * 2) * 0.3,
            ]}
          >
            <sphereGeometry args={[0.055, 12, 12]} />
            <meshStandardMaterial color="#9be9fb" roughness={0.72} />
          </mesh>
        ))}
      </group>

      <mesh position={[0, 2.7, -0.3]} rotation={[0.13, 0, 0]}>
        <capsuleGeometry args={[0.1, 1.78, 8, 22]} />
        <meshPhysicalMaterial
          color="#dceff5"
          roughness={0.12}
          metalness={0.76}
        />
      </mesh>

      <RoundedBox
        args={[0.9, 0.23, 0.26]}
        radius={0.1}
        smoothness={5}
        position={[0, 3.56, -0.08]}
        rotation={[0.13, 0, 0]}
      >
        <meshPhysicalMaterial
          color="#102b38"
          roughness={0.22}
          metalness={0.55}
          clearcoat={0.7}
        />
      </RoundedBox>
    </group>
  );
}

function ProductStory({
  progressRef,
  pointerRef,
  onReady,
}: SceneProps) {
  const product = useRef<THREE.Group>(null);
  const productRoot = useRef<THREE.Group>(null);
  const shadow = useRef<THREE.Group>(null);
  const hasReportedReady = useRef(false);

  const { camera, size } = useThree();

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

  const chapterTargets = useMemo(
    () => [
      {
        start: 0,
        end: 0.16,
        x: 0,
        y: isMobile ? -0.62 : -0.12,
        z: 0,
        scale: isMobile ? 0.59 : isTablet ? 0.79 : 0.94,
        rx: -0.02,
        ry: -0.08,
      },
      {
        start: 0.16,
        end: 0.33,
        x: isMobile ? 0.18 : 1.05,
        y: isMobile ? -0.38 : -0.02,
        z: 0.72,
        scale: isMobile ? 0.68 : isTablet ? 0.9 : 1.08,
        rx: -0.08,
        ry: 0.35,
      },
      {
        start: 0.33,
        end: 0.5,
        x: isMobile ? -0.24 : -1.28,
        y: isMobile ? -0.18 : 0.12,
        z: 1.38,
        scale: isMobile ? 0.78 : isTablet ? 1.02 : 1.22,
        rx: -0.13,
        ry: -0.54,
      },
      {
        start: 0.5,
        end: 0.67,
        x: isMobile ? 0.22 : 1.45,
        y: isMobile ? -0.55 : -0.36,
        z: 1.85,
        scale: isMobile ? 0.86 : isTablet ? 1.12 : 1.34,
        rx: 0.08,
        ry: 0.64,
      },
      {
        start: 0.67,
        end: 0.84,
        x: isMobile ? -0.16 : -1.02,
        y: isMobile ? -0.34 : 0.08,
        z: 0.95,
        scale: isMobile ? 0.69 : isTablet ? 0.92 : 1.12,
        rx: -0.04,
        ry: -0.3,
      },
      {
        start: 0.84,
        end: 1,
        x: 0,
        y: isMobile ? -0.45 : -0.14,
        z: 0.22,
        scale: isMobile ? 0.61 : isTablet ? 0.82 : 0.98,
        rx: -0.01,
        ry: 0.03,
      },
    ],
    [isMobile, isTablet],
  );

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

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

    let current = chapterTargets[0];
    let next = chapterTargets[1];
    let local = 0;

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

      if (progress >= chapter.start && progress <= chapter.end) {
        current = chapter;
        next = chapterTargets[Math.min(index + 1, chapterTargets.length - 1)];

        local = smoothStep(
          chapter.start,
          chapter.end,
          progress,
        );

        break;
      }
    }

    const targetX = THREE.MathUtils.lerp(current.x, next.x, local);
    const targetY = THREE.MathUtils.lerp(current.y, next.y, local);
    const targetZ = THREE.MathUtils.lerp(current.z, next.z, local);
    const targetScale = THREE.MathUtils.lerp(
      current.scale,
      next.scale,
      local,
    );
    const targetRX = THREE.MathUtils.lerp(current.rx, next.rx, local);
    const targetRY = THREE.MathUtils.lerp(current.ry, next.ry, local);

    if (productRoot.current) {
      productRoot.current.position.x = damp(
        productRoot.current.position.x,
        targetX + pointer.x * (isMobile ? 0.015 : 0.055),
        3.5,
        delta,
      );

      productRoot.current.position.y = damp(
        productRoot.current.position.y,
        targetY - pointer.y * (isMobile ? 0.012 : 0.035),
        3.5,
        delta,
      );

      productRoot.current.position.z = damp(
        productRoot.current.position.z,
        targetZ,
        3.5,
        delta,
      );

      productRoot.current.scale.setScalar(
        damp(
          productRoot.current.scale.x,
          targetScale,
          3.6,
          delta,
        ),
      );
    }

    if (product.current) {
      product.current.rotation.x = damp(
        product.current.rotation.x,
        targetRX + pointer.y * (isMobile ? 0.01 : 0.025),
        3.3,
        delta,
      );

      product.current.rotation.y = damp(
        product.current.rotation.y,
        targetRY + pointer.x * (isMobile ? 0.012 : 0.038),
        3.3,
        delta,
      );
    }

    const desktopCameraZ =
      progress < 0.2
        ? 10
        : progress < 0.7
          ? 9.2
          : 9.75;

    const cameraZ =
      desktopCameraZ +
      (isMobile ? 3.2 : isTablet ? 1.35 : 0);

    camera.position.x = damp(camera.position.x, 0, 3, delta);
    camera.position.y = damp(
      camera.position.y,
      isMobile ? 0.5 : 0.18,
      3,
      delta,
    );
    camera.position.z = damp(camera.position.z, cameraZ, 3, delta);

    if (camera instanceof THREE.PerspectiveCamera) {
      const targetFov = isMobile ? 42 : isTablet ? 40 : 36;

      camera.fov = damp(camera.fov, targetFov, 3, delta);
      camera.updateProjectionMatrix();
    }

    camera.lookAt(0, isMobile ? -0.24 : 0.08, 0);

    if (shadow.current) {
      shadow.current.position.x = damp(
        shadow.current.position.x,
        targetX * 0.55,
        3.2,
        delta,
      );
    }
  });

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

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

      <directionalLight
        position={[-4, 2, 5]}
        intensity={1.7}
        color="#6ddcf8"
      />

      <pointLight
        position={[0, -1.3, 4]}
        intensity={8}
        distance={12}
        color="#57d8f8"
      />

      <group ref={productRoot}>
        <Float
          speed={0.55}
          floatIntensity={0.08}
          rotationIntensity={0}
        >
          <group ref={product}>
            <ProductModel />
          </group>
        </Float>
      </group>

      <group ref={shadow}>
        <ContactShadows
          position={[0, -2.05, 0]}
          opacity={0.22}
          scale={7.5}
          blur={3.6}
          far={5}
        />
      </group>

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

export default function LarkShine3DScene(props: SceneProps) {
  return (
    <Canvas
      dpr={[1, 1.55]}
      camera={{
        position: [0, 0.18, 10],
        fov: 36,
        near: 0.1,
        far: 100,
      }}
      gl={{
        antialias: true,
        alpha: true,
        powerPreference: "high-performance",
      }}
      shadows
    >
      <Suspense fallback={null}>
        <ProductStory {...props} />
      </Suspense>
    </Canvas>
  );
}
