"use client";

import { RoundedBox } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { useMemo, useRef } from "react";
import * as THREE from "three";

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

function getStoryProgress() {
  const story = document.getElementById("home");

  if (!story) return 0;

  const rect = story.getBoundingClientRect();
  const distance = Math.max(rect.height - window.innerHeight, 1);

  return clamp(-rect.top / distance);
}

function WaterDroplets() {
  const droplets = useMemo(
    () =>
      Array.from({ length: 26 }, (_, index) => ({
        x: -3.7 + ((index * 1.73) % 7.4),
        y: -3.5 + ((index * 2.11) % 7),
        size: 0.045 + (index % 5) * 0.022,
        speed: 0.08 + (index % 6) * 0.025,
      })),
    [],
  );

  return (
    <group position={[0, 0, 0.16]}>
      {droplets.map((drop, index) => (
        <Droplet
          key={index}
          initialX={drop.x}
          initialY={drop.y}
          size={drop.size}
          speed={drop.speed}
          offset={index * 0.37}
        />
      ))}
    </group>
  );
}

function Droplet({
  initialX,
  initialY,
  size,
  speed,
  offset,
}: {
  initialX: number;
  initialY: number;
  size: number;
  speed: number;
  offset: number;
}) {
  const ref = useRef<THREE.Mesh>(null);

  useFrame((state) => {
    if (!ref.current) return;

    const progress = getStoryProgress();
    const time = state.clock.elapsedTime;

    const cleanedBoundary = THREE.MathUtils.lerp(4.2, -4.2, progress);
    const visible = initialY < cleanedBoundary + 1;

    ref.current.visible = visible;

    const falling =
      initialY -
      ((time * speed + offset) % 1.7) * 1.35;

    ref.current.position.set(initialX, falling, 0);

    const stretch = 1 + Math.sin(time * 1.8 + offset) * 0.15;
    ref.current.scale.set(1, stretch, 1);
  });

  return (
    <mesh ref={ref}>
      <sphereGeometry args={[size, 16, 16]} />

      <meshPhysicalMaterial
        color="#d9f4ff"
        roughness={0.05}
        metalness={0}
        transmission={0.8}
        thickness={0.25}
        transparent
        opacity={0.78}
        clearcoat={1}
      />
    </mesh>
  );
}

function Foam() {
  const bubbles = useMemo(
    () =>
      Array.from({ length: 24 }, (_, index) => ({
        x: -3.35 + (index / 23) * 6.7,
        y: Math.sin(index * 1.8) * 0.1,
        size: 0.09 + (index % 4) * 0.025,
      })),
    [],
  );

  return (
    <group>
      {bubbles.map((bubble, index) => (
        <mesh
          key={index}
          position={[bubble.x, bubble.y, 0]}
          scale={bubble.size}
        >
          <sphereGeometry args={[1, 12, 12]} />

          <meshPhysicalMaterial
            color="#ffffff"
            roughness={0.15}
            transmission={0.35}
            transparent
            opacity={0.9}
            clearcoat={1}
          />
        </mesh>
      ))}
    </group>
  );
}

function Squeegee() {
  const group = useRef<THREE.Group>(null);
  const handle = useRef<THREE.Group>(null);
  const foam = useRef<THREE.Group>(null);
  const glow = useRef<THREE.PointLight>(null);

  useFrame((state, delta) => {
    if (
      !group.current ||
      !handle.current ||
      !foam.current ||
      !glow.current
    ) {
      return;
    }

    const progress = getStoryProgress();
    const time = state.clock.elapsedTime;

    /*
     * Four horizontal cleaning passes.
     * The blade stays against the glass rather than flying toward the camera.
     */
    const passCount = 4;
    const passValue = progress * passCount;
    const passIndex = Math.min(
      Math.floor(passValue),
      passCount - 1,
    );
    const localProgress = passValue - Math.floor(passValue);

    const leftToRight = passIndex % 2 === 0;
    const horizontalProgress = leftToRight
      ? localProgress
      : 1 - localProgress;

    const targetX = THREE.MathUtils.lerp(
      -3.55,
      3.55,
      horizontalProgress,
    );

    const targetY = THREE.MathUtils.lerp(
      3.65,
      -3.65,
      progress,
    );

    const smoothing = 1 - Math.pow(0.00001, delta);

    group.current.position.x = THREE.MathUtils.lerp(
      group.current.position.x,
      targetX,
      smoothing,
    );

    group.current.position.y = THREE.MathUtils.lerp(
      group.current.position.y,
      targetY,
      smoothing,
    );

    group.current.position.z = THREE.MathUtils.lerp(
      group.current.position.z,
      0.48,
      smoothing,
    );

    /*
     * Small perspective tilt keeps it visibly 3D,
     * but it remains pressed against the cleaning surface.
     */
    group.current.rotation.x = THREE.MathUtils.lerp(
      group.current.rotation.x,
      -0.08,
      0.08,
    );

    group.current.rotation.y = THREE.MathUtils.lerp(
      group.current.rotation.y,
      leftToRight ? -0.08 : 0.08,
      0.08,
    );

    group.current.rotation.z = THREE.MathUtils.lerp(
      group.current.rotation.z,
      leftToRight ? -0.035 : 0.035,
      0.08,
    );

    const edgeSlowdown =
      Math.sin(localProgress * Math.PI);

    const targetScale =
      1.03 + edgeSlowdown * 0.06;

    group.current.scale.setScalar(
      THREE.MathUtils.lerp(
        group.current.scale.x,
        targetScale,
        0.08,
      ),
    );

    handle.current.rotation.z =
      leftToRight ? -0.19 : -0.13;

    handle.current.rotation.x =
      Math.sin(time * 0.7) * 0.015;

    foam.current.scale.x =
      0.92 + Math.sin(time * 3.2) * 0.025;

    foam.current.position.y =
      0.25 + Math.sin(time * 2.4) * 0.012;

    glow.current.intensity =
      10 + edgeSlowdown * 10;
  });

  return (
    <group ref={group} position={[-3.55, 3.65, 0.48]}>
      <group ref={foam} position={[0, 0.25, 0.02]}>
        <Foam />
      </group>

      <RoundedBox
        args={[7.15, 0.36, 0.48]}
        radius={0.14}
        smoothness={5}
      >
        <meshPhysicalMaterial
          color="#078fc9"
          roughness={0.14}
          metalness={0.13}
          clearcoat={1}
          clearcoatRoughness={0.04}
          emissive="#006b9c"
          emissiveIntensity={0.1}
        />
      </RoundedBox>

      <RoundedBox
        position={[0, -0.24, 0.04]}
        args={[7, 0.22, 0.24]}
        radius={0.07}
        smoothness={4}
      >
        <meshStandardMaterial
          color="#12364f"
          roughness={0.34}
        />
      </RoundedBox>

      <mesh position={[0, -0.39, 0.09]}>
        <boxGeometry args={[6.9, 0.045, 0.13]} />
        <meshBasicMaterial
          color="#e8fbff"
          transparent
          opacity={0.95}
        />
      </mesh>

      <group ref={handle} position={[0, 0.12, 0.32]}>
        <RoundedBox
          position={[0.48, 1.85, 0]}
          rotation={[0, 0, -0.17]}
          args={[0.68, 4.05, 0.7]}
          radius={0.23}
          smoothness={5}
        >
          <meshPhysicalMaterial
            color="#f9fdff"
            roughness={0.13}
            metalness={0.08}
            clearcoat={1}
          />
        </RoundedBox>

        <RoundedBox
          position={[0.82, 3.72, 0]}
          rotation={[0, 0, -0.17]}
          args={[0.92, 1.25, 0.92]}
          radius={0.29}
          smoothness={5}
        >
          <meshPhysicalMaterial
            color="#079bd8"
            roughness={0.15}
            clearcoat={1}
          />
        </RoundedBox>
      </group>

      <pointLight
        ref={glow}
        position={[0, -0.15, 1]}
        intensity={12}
        distance={7}
        color="#8de2ff"
      />
    </group>
  );
}

export default function GlassCleaner() {
  const glass = useRef<THREE.Group>(null);

  useFrame((state) => {
    if (!glass.current) return;

    const progress = getStoryProgress();

    glass.current.rotation.y =
      Math.sin(state.clock.elapsedTime * 0.2) * 0.012;

    glass.current.position.x =
      window.innerWidth > 900 ? 2.7 : 0;

    glass.current.position.y =
      window.innerWidth > 900 ? -0.15 : 1.15;

    glass.current.scale.setScalar(
      window.innerWidth > 900 ? 1 : 0.73,
    );

    const finishingTilt = clamp((progress - 0.82) / 0.18);

    glass.current.rotation.z =
      finishingTilt * -0.045;
  });

  return (
    <group ref={glass}>
      <RoundedBox
        args={[8.7, 9, 0.28]}
        radius={0.22}
        smoothness={6}
        receiveShadow
      >
        <meshPhysicalMaterial
          color="#dff6ff"
          roughness={0.08}
          metalness={0.02}
          transmission={0.78}
          thickness={0.35}
          transparent
          opacity={0.5}
          clearcoat={1}
        />
      </RoundedBox>

      <RoundedBox
        position={[0, 0, -0.22]}
        args={[9.15, 9.45, 0.2]}
        radius={0.24}
        smoothness={5}
      >
        <meshStandardMaterial
          color="#d6e6eb"
          roughness={0.55}
          metalness={0.1}
        />
      </RoundedBox>

      <mesh position={[0, 0, -0.34]}>
        <planeGeometry args={[8.55, 8.85]} />

        <meshStandardMaterial
          color="#c6d5d8"
          roughness={0.98}
          transparent
          opacity={0.52}
        />
      </mesh>

      <WaterDroplets />
      <Squeegee />

      <mesh position={[-3.9, 0, 0.28]}>
        <boxGeometry args={[0.07, 8.6, 0.08]} />
        <meshBasicMaterial color="#ffffff" transparent opacity={0.9} />
      </mesh>

      <mesh position={[3.9, 0, 0.28]}>
        <boxGeometry args={[0.07, 8.6, 0.08]} />
        <meshBasicMaterial color="#ffffff" transparent opacity={0.9} />
      </mesh>
    </group>
  );
}
