"use client"

/**
 * Full-app ambient Three.js background.
 *
 * Design goals (per Design Guidelines):
 * - Subtle, not competing with foreground content.
 * - Mobile-first: low DPR, low geometry detail, 60fps capped.
 * - Responsive: `<Canvas>` auto-resizes; the scene's look doesn't depend
 *   on a hard-coded viewport size.
 * - Respects `prefers-reduced-motion` (freezes animation to a static
 *   frame while still rendering the shapes).
 * - Cheap to render: only a handful of low-poly icosahedrons using
 *   MeshDistortMaterial. No post-processing, no shadows, no lights
 *   expensive enough to matter.
 *
 * Palette is pulled from the app's sunset shader-button palette so the
 * whole app feels visually cohesive:
 *   hot pink, orange, amber, deep violet.
 */

import { Canvas, useFrame } from "@react-three/fiber"
import { MeshDistortMaterial } from "@react-three/drei"
import { useMemo, useRef } from "react"
import type { Mesh } from "three"

/**
 * Palette sampled directly from the ZunoChat brand lockup — the same
 * rainbow arc that flows across the chat-bubble icon. Keeping the
 * order intentional (cool → warm) makes adjacent blobs feel like they
 * belong to a continuous gradient even though they're individual
 * meshes. These hex values match the CSS `--brand-gradient` stops in
 * `globals.css`.
 */
const PALETTE = [
  "#22c1ee", // cyan
  "#5b8def", // royal blue
  "#e6299b", // magenta
  "#ff4e9e", // hot pink
  "#ff8c3a", // orange
  "#ffd23b", // yellow
] as const

/** Configuration for each floating blob. */
type BlobConfig = {
  position: [number, number, number]
  scale: number
  color: (typeof PALETTE)[number]
  speed: number
  distort: number
  /** Phase offset so blobs don't all bob in sync. */
  phase: number
}

/**
 * Deterministic blob layout — scattered across the viewport with varied
 * sizes + colors. Hand-tuned rather than random so the composition is
 * stable between renders (no hydration flicker, no SSR drift).
 */
const BLOBS: BlobConfig[] = [
  // Top-left: cool cyan, echoes the top of the chat-bubble mark.
  {
    position: [-2.4, 1.3, -1.5],
    scale: 1.6,
    color: PALETTE[0],
    speed: 0.32,
    distort: 0.35,
    phase: 0,
  },
  // Upper-right: royal blue, bridging cyan → magenta.
  {
    position: [2.3, 1.1, -2.2],
    scale: 1.2,
    color: PALETTE[1],
    speed: 0.4,
    distort: 0.4,
    phase: 1.1,
  },
  // Hero center-right: the magenta signature of the brand, biggest blob.
  {
    position: [2.4, -0.6, -1.2],
    scale: 2.0,
    color: PALETTE[2],
    speed: 0.26,
    distort: 0.3,
    phase: 2.0,
  },
  // Bottom-center: hot pink, balances the warm half of the arc.
  {
    position: [0.2, -1.8, -2.0],
    scale: 1.4,
    color: PALETTE[3],
    speed: 0.42,
    distort: 0.42,
    phase: 2.8,
  },
  // Bottom-left: orange, closes the arc toward the viewer's gaze.
  {
    position: [-1.6, -0.5, -2.5],
    scale: 1.1,
    color: PALETTE[4],
    speed: 0.5,
    distort: 0.45,
    phase: 3.6,
  },
  // Far-back accent: yellow, small + pushed deep so it reads as a
  // distant sun without competing with foreground content.
  {
    position: [1.2, 1.8, -3.2],
    scale: 0.85,
    color: PALETTE[5],
    speed: 0.55,
    distort: 0.5,
    phase: 4.3,
  },
]

/**
 * A single distorted sphere that gently bobs + rotates. We keep geometry
 * at detail level 1 (very low poly) because MeshDistortMaterial does its
 * work in the shader — more polys would cost fps without visible gain.
 */
function Blob({ config, reducedMotion }: { config: BlobConfig; reducedMotion: boolean }) {
  const ref = useRef<Mesh>(null)

  useFrame(({ clock }) => {
    if (!ref.current) return
    if (reducedMotion) return
    const t = clock.getElapsedTime() * config.speed + config.phase
    // Gentle vertical bob + slow roll. Amplitude kept tiny so the blobs
    // never drift into the frame edges and break the composition.
    ref.current.position.y = config.position[1] + Math.sin(t) * 0.25
    ref.current.position.x = config.position[0] + Math.cos(t * 0.7) * 0.15
    ref.current.rotation.x = t * 0.15
    ref.current.rotation.y = t * 0.2
  })

  return (
    <mesh ref={ref} position={config.position} scale={config.scale}>
      <icosahedronGeometry args={[1, 1]} />
      <MeshDistortMaterial
        color={config.color}
        speed={reducedMotion ? 0 : 1.8}
        distort={config.distort}
        roughness={0.5}
        metalness={0.15}
        transparent
        // Slightly higher opacity so the blobs remain readable even
        // behind the app's translucent surfaces (headers, pills, etc.).
        opacity={0.9}
      />
    </mesh>
  )
}

/**
 * Reads `prefers-reduced-motion` once at mount. We don't subscribe to
 * changes because toggling it mid-session is exceedingly rare and the
 * subscription costs a listener on every mount.
 */
function usePrefersReducedMotion(): boolean {
  return useMemo(() => {
    if (typeof window === "undefined") return false
    return window.matchMedia("(prefers-reduced-motion: reduce)").matches
  }, [])
}

export function ThreeBackground() {
  const reducedMotion = usePrefersReducedMotion()

  return (
    // Fixed, full-viewport, BEHIND all content. `pointer-events-none` so
    // it never steals clicks/taps from UI above. `aria-hidden` because
    // it's purely decorative.
    <div
      aria-hidden="true"
      className="pointer-events-none fixed inset-0 -z-10 overflow-hidden"
    >
      {/* Soft base gradient — two large radial washes in the sunset
          palette. The final solid layer intentionally uses the `bg-
          background` Tailwind utility (backed by the oklch design token)
          rather than an inline `hsl(var(--background))` expression,
          because the tokens are defined in oklch and wrapping them in
          hsl() yields an invalid color → the previous version fell back
          to transparent, which made the canvas look empty on top of
          the opaque body bg. */}
      <div
        className="absolute inset-0 bg-background"
        style={{
          // Three large radial washes in the ZunoChat arc (cyan → magenta
          // → orange), used to tint the neutral bg so the scene feels
          // branded even when no blobs are on screen (ultra-low-end GPU
          // fallback, or SSR flash before hydration).
          backgroundImage:
            "radial-gradient(1200px 800px at 12% 8%, rgba(34,193,238,0.22), transparent 60%), radial-gradient(1000px 700px at 88% 92%, rgba(230,41,155,0.22), transparent 60%), radial-gradient(900px 700px at 50% 110%, rgba(255,140,58,0.18), transparent 60%)",
        }}
      />
      <Canvas
        // Cap DPR on mobile for perf — [1, 1.5] means we never waste GPU
        // rendering extra pixels on retina devices for a decorative bg.
        dpr={[1, 1.5]}
        // Orthographic-style FOV; blobs are close to camera so they feel
        // ambient rather than scenic.
        camera={{ position: [0, 0, 5], fov: 55 }}
        // No shadows / no tone mapping — pure look is fine for decor.
        gl={{ antialias: true, alpha: true, powerPreference: "low-power" }}
        // Let React Three Fiber stop rendering when the tab is hidden.
        frameloop={reducedMotion ? "demand" : "always"}
      >
        {/* Minimal lighting — ambient for base color, one soft directional
            for a hint of shape. MeshDistortMaterial has its own shading
            so we don't need much. */}
        <ambientLight intensity={0.8} />
        <directionalLight position={[3, 4, 5]} intensity={0.6} />
        {BLOBS.map((cfg, i) => (
          <Blob key={i} config={cfg} reducedMotion={reducedMotion} />
        ))}
      </Canvas>
    </div>
  )
}
