import { Suspense, useEffect, useRef, useState, type ReactNode } from 'react';
import { Icon } from '@iconify/react';
import { Canvas } from '@react-three/fiber';
import { Bounds, ContactShadows, OrbitControls, useProgress } from '@react-three/drei';
import { EffectComposer, Vignette } from '@react-three/postprocessing';

/**
 * Shared 3D scene wrapper for object viewers. Provides a consistent camera,
 * lighting, orbit controls, a soft contact shadow, and subtle post-processing
 * (bloom + vignette). Children are wrapped in a Suspense boundary so loading
 * assets (textures, models) can suspend without blanking the page.
 *
 * The camera is fitted to the bounds of the content on mount. `Bounds` sits
 * inside the Suspense boundary, so it only mounts once the (suspending) content
 * has loaded and its geometry is present.
 *
 * `autoRotate` and `enablePan` tune the orbit controls (a tabletop view, for
 * example, pans instead of rotating). `fullscreen` adds a toggle button that
 * expands the scene to the full screen.
 */
export default function Scene({
  children,
  autoRotate = true,
  enablePan = false,
  fullscreen = false,
  overlay,
  shadowScale = 22,
}: {
  children: ReactNode;
  autoRotate?: boolean;
  enablePan?: boolean;
  fullscreen?: boolean;
  /** HTML rendered inside the scene container (e.g. controls), shown in fullscreen. */
  overlay?: ReactNode;
  /** Contact shadow plane size in world units; defaults to a generous 22. */
  shadowScale?: number;
}) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isFullscreen, setIsFullscreen] = useState(false);

  useEffect(() => {
    const onChange = () => setIsFullscreen(!!document.fullscreenElement);
    document.addEventListener('fullscreenchange', onChange);
    return () => document.removeEventListener('fullscreenchange', onChange);
  }, []);

  const toggleFullscreen = () => {
    if (document.fullscreenElement) void document.exitFullscreen();
    else void containerRef.current?.requestFullscreen();
  };

  return (
    <div
      ref={containerRef}
      className="relative h-80 w-full overflow-hidden rounded-lg bg-linear-to-b from-zinc-300 to-zinc-400"
    >
      <LoadingOverlay />
      {fullscreen && (
        <button
          onClick={toggleFullscreen}
          title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
          aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
          className="absolute right-2 top-2 z-10 flex h-8 w-8 items-center justify-center rounded-md border border-zinc-700 bg-zinc-900/80 text-zinc-300 backdrop-blur transition hover:bg-zinc-800 hover:text-zinc-100"
        >
          <Icon icon={isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen'} className="h-5 w-5" />
        </button>
      )}
      {overlay}
      <Canvas
        camera={{ position: [2.2, 1.8, 2.6], fov: 40 }}
        dpr={[1, 2]}
        gl={{ antialias: true, alpha: true }}
      >
        <ambientLight intensity={0.5} />
        <directionalLight position={[4, 6, 3]} intensity={1.4} />
        <directionalLight position={[-4, 2, -3]} intensity={0.4} color="#b3c7ff" />
        <pointLight position={[0, 3, 0]} intensity={0.3} />

        <Suspense fallback={null}>
          <Bounds fit observe clip>{children}</Bounds>
        </Suspense>

        <ContactShadows
          position={[0, -0.01, 0]}
          opacity={0.2}
          scale={shadowScale}
          blur={shadowScale * 0.005}
          far={shadowScale * 0.01}
          resolution={1024}
          color="#000000"
        />
        <OrbitControls
          enablePan={enablePan}
          minDistance={0.01}
          maxDistance={8}
          autoRotate={autoRotate}
          makeDefault
        />

        <EffectComposer>
          <Vignette eskil={false} offset={0.25} darkness={0.6} />
        </EffectComposer>
      </Canvas>
    </div>
  );
}

/**
 * A loading overlay shown while assets (textures, models, traces) are being
 * fetched. Reads drei's global progress store, which tracks every loader in
 * the scene, so it works outside the Canvas. Hidden once loading completes.
 */
function LoadingOverlay() {
  const { active, progress, item, loaded, total } = useProgress();
  if (!active) return null;
  return (
    <div className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-2 bg-zinc-950/70 text-sm text-zinc-300">
      <div className="flex items-center gap-2">
        <span className="h-4 w-4 animate-spin rounded-full border-2 border-zinc-600 border-t-zinc-200" />
        <span>Loading assets… {Math.round(progress)}%</span>
      </div>
      <span className="max-w-80 truncate text-xs text-zinc-500">
        {loaded}/{total} {item}
      </span>
    </div>
  );
}
