'use client';

/* eslint-disable @typescript-eslint/no-namespace */
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Canvas, extend, useFrame, useThree } from '@react-three/fiber';
import { shaderMaterial } from '@react-three/drei';
import * as THREE from 'three';
import { useBackground } from '../../context/BackgroundContext';
import type { ColorPalette } from '../../types/theme';
import { PALETTES } from '../../types/theme';

// R3F JSX augmentation for our custom shader material element
declare module 'react' {
  namespace JSX {
    interface IntrinsicElements {
      mesh: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & Record<string, unknown>;
      planeGeometry: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & Record<string, unknown>;
      livingFlowMaterial: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & Record<string, unknown>;
    }
  }
}

/**
 * Living background: "breathing" metaballs + soft aurora flow.
 * Theme-aware (BackgroundContext) and mouse-reactive with a gentle, premium motion profile.
 *
 * This intentionally matches the project's "blackMagic" shader vibe family, but feels lighter,
 * more organic, and less turbulent.
 */
const LivingFlowMaterial = shaderMaterial(
  {
    uTime: 0,
    uColorA: new THREE.Color('#DBEAFE'),
    uColorB: new THREE.Color('#E0E7FF'),
    uColorC: new THREE.Color('#CCFBF1'),
    uColorD: new THREE.Color('#F3E8FF'),
    uBackground: new THREE.Color('#FFFFFF'),
    uAccent: new THREE.Color('#FE6C00'),
    uMouse: new THREE.Vector2(0.5, 0.5),
    uResolution: new THREE.Vector2(1920, 1080),
    uDarkMode: 0.0,
    uMotion: 1.0,
  },
  // Vertex Shader
  `
    varying vec2 vUv;
    uniform float uTime;
    uniform float uMotion;

    void main() {
      vUv = uv;
      vec3 pos = position;
      float t = uTime * 0.35 * uMotion;
      float wave = (sin(pos.x * 1.8 + t) * cos(pos.y * 1.8 - t)) * 0.015;
      pos.z += wave;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  // Fragment Shader
  `
    varying vec2 vUv;

    uniform float uTime;
    uniform vec3 uColorA;
    uniform vec3 uColorB;
    uniform vec3 uColorC;
    uniform vec3 uColorD;
    uniform vec3 uBackground;
    uniform vec3 uAccent;
    uniform vec2 uMouse;
    uniform vec2 uResolution;
    uniform float uDarkMode;
    uniform float uMotion;

    // --- Helpers ---
    float hash(vec2 p) {
      return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
    }

    float noise(vec2 p) {
      vec2 i = floor(p);
      vec2 f = fract(p);
      float a = hash(i);
      float b = hash(i + vec2(1.0, 0.0));
      float c = hash(i + vec2(0.0, 1.0));
      float d = hash(i + vec2(1.0, 1.0));
      vec2 u = f * f * (3.0 - 2.0 * f);
      return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
    }

    float fbm(vec2 p) {
      float v = 0.0;
      float a = 0.5;
      mat2 m = mat2(1.6, 1.2, -1.2, 1.6);
      for (int i = 0; i < 4; i++) {
        v += a * noise(p);
        p = m * p;
        a *= 0.5;
      }
      return v;
    }

    float metaball(vec2 p, vec2 c, float r) {
      vec2 d = p - c;
      float dd = dot(d, d) + 0.001; // avoid divide-by-zero
      return (r * r) / dd;
    }

    vec3 blendScreen(vec3 base, vec3 blend) {
      return 1.0 - (1.0 - base) * (1.0 - blend);
    }

    void main() {
      vec2 uv = vUv;
      float aspect = uResolution.x / max(uResolution.y, 1.0);
      vec2 p = (uv - 0.5) * vec2(aspect, 1.0);

      float t = uTime * 0.55 * uMotion;

      // Mouse: gentle "breathing pull" (keeps it premium, not twitchy)
      vec2 m = (uMouse - 0.5) * vec2(aspect, 1.0);
      float mDist = length(p - m);
      p += (m - p) * smoothstep(0.9, 0.0, mDist) * 0.05;

      // Slow domain warp for aurora-like flow
      vec2 warpP = p * 1.1;
      vec2 q = vec2(fbm(warpP + vec2(0.0, t)), fbm(warpP + vec2(4.1, -t)));
      vec2 r = vec2(fbm(warpP + 1.7 * q + vec2(1.7, 9.2)), fbm(warpP + 1.7 * q + vec2(8.3, 2.8)));
      float flow = fbm(warpP + 1.8 * r);

      // Metaballs: 4 drifting sources + 1 mouse source
      vec2 c1 = vec2( 0.35 * sin(t * 0.8),  0.28 * cos(t * 0.7));
      vec2 c2 = vec2(-0.32 * cos(t * 0.6),  0.26 * sin(t * 0.9));
      vec2 c3 = vec2( 0.22 * sin(t * 0.5 + 1.9), -0.30 * cos(t * 0.75 + 0.6));
      vec2 c4 = vec2(-0.25 * sin(t * 0.65 + 2.7), -0.18 * sin(t * 0.85 + 1.1));

      float field =
        metaball(p, c1, 0.35) +
        metaball(p, c2, 0.30) +
        metaball(p, c3, 0.26) +
        metaball(p, c4, 0.28) +
        metaball(p, m * 0.9, 0.18);

      // Normalize-ish and combine with flow
      float height = clamp(field * 0.18 + flow * 0.45, 0.0, 1.0);

      // Color bands (soft, brandy)
      vec3 col = uBackground;
      col = mix(col, uColorA, smoothstep(0.10, 0.35, height));
      col = mix(col, uColorB, smoothstep(0.25, 0.55, height));
      col = mix(col, uColorC, smoothstep(0.45, 0.75, height));
      col = mix(col, uColorD, smoothstep(0.65, 0.95, height));

      // Accent bloom only on the highest peaks
      float peak = smoothstep(0.78, 0.98, height);
      col = mix(col, blendScreen(col, uAccent), peak * mix(0.12, 0.18, uDarkMode));

      // Vignette (focus)
      float v = 1.0 - smoothstep(0.35, 1.25, length(p));
      col *= mix(0.90, 0.75, uDarkMode) + v * mix(0.12, 0.22, uDarkMode);

      // Grain/dither to avoid banding (tiny, classy)
      float g = (hash(uv * (uResolution.xy / 2.0) + uTime) - 0.5);
      col += g * mix(0.012, 0.020, uDarkMode);

      col = clamp(col, 0.0, 1.0);
      gl_FragColor = vec4(col, 1.0);
    }
  `
);

extend({ LivingFlowMaterial });

const LivingScene = ({ palette, motionEnabled }: { palette: ColorPalette; motionEnabled: boolean }) => {
  const materialRef = useRef<any>(null);
  const mouse = useRef(new THREE.Vector2(0.5, 0.5));
  const targetMouse = useRef(new THREE.Vector2(0.5, 0.5));
  const { viewport, size } = useThree();
  const { darkMode } = useBackground();

  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      targetMouse.current.set(e.clientX / window.innerWidth, 1.0 - e.clientY / window.innerHeight);
    };
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  useFrame((state, delta) => {
    if (!materialRef.current) return;

    const motion = motionEnabled ? 1.0 : 0.0;
    materialRef.current.uMotion = motion;
    materialRef.current.uTime += delta;
    materialRef.current.uResolution.set(size.width, size.height);

    // Gentle mouse smoothing
    mouse.current.lerp(targetMouse.current, motionEnabled ? 0.05 : 1.0);
    materialRef.current.uMouse.copy(mouse.current);

    // Smooth dark mode transition
    const targetDarkMode = darkMode ? 1.0 : 0.0;
    materialRef.current.uDarkMode = THREE.MathUtils.lerp(
      materialRef.current.uDarkMode,
      targetDarkMode,
      delta * 2.0
    );

    // Smooth palette transitions
    const colorLerp = delta * 1.6;
    materialRef.current.uColorA.lerp(new THREE.Color(palette.colorA), colorLerp);
    materialRef.current.uColorB.lerp(new THREE.Color(palette.colorB), colorLerp);
    materialRef.current.uColorC.lerp(new THREE.Color(palette.colorC), colorLerp);
    materialRef.current.uColorD.lerp(new THREE.Color(palette.colorD), colorLerp);
    materialRef.current.uBackground.lerp(new THREE.Color(palette.background), colorLerp);

    // Accent: slightly warmer in dark mode, slightly punchier in light
    const accent = darkMode ? new THREE.Color('#FE6C00') : new THREE.Color('#0052CC');
    materialRef.current.uAccent.lerp(accent, colorLerp);
  });

  return (
    <mesh scale={[viewport.width, viewport.height, 1]} position={[0, 0, 0]}>
      <planeGeometry args={[1, 1, 64, 64]} />
      <livingFlowMaterial ref={materialRef} />
    </mesh>
  );
};

const LivingBackground: React.FC = () => {
  const { currentTheme, darkMode } = useBackground();
  // Use PALETTES based on current section theme
  const activePalette = PALETTES[currentTheme][darkMode ? 'dark' : 'light'];
  const [motionEnabled, setMotionEnabled] = useState(true);

  useEffect(() => {
    // Respect reduced motion preference (and allow it to change live)
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    const update = () => setMotionEnabled(!mq.matches);
    update();
    mq.addEventListener?.('change', update);
    return () => mq.removeEventListener?.('change', update);
  }, []);

  // Inline SVG noise (no external request; matches the rest of the repo vibe)
  const noiseBg = useMemo(() => {
    const svg = encodeURIComponent(
      `<svg viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
        <filter id="n">
          <feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="4" stitchTiles="stitch"/>
        </filter>
        <rect width="100%" height="100%" filter="url(#n)"/>
      </svg>`
    );
    return `url("data:image/svg+xml,${svg}")`;
  }, []);

  return (
    <div className="relative h-full w-full overflow-hidden pointer-events-none">
      <Canvas
        dpr={[1, 2]}
        camera={{ position: [0, 0, 1], fov: 50 }}
        gl={{
          antialias: false,
          alpha: true,
          powerPreference: 'high-performance',
          stencil: false,
          depth: false,
        }}
      >
        <LivingScene palette={activePalette} motionEnabled={motionEnabled} />
      </Canvas>

      {/* Grain layer */}
      <div
        className="absolute inset-0 z-10 pointer-events-none mix-blend-overlay"
        style={{
          opacity: 0.02,
          backgroundImage: noiseBg,
        }}
      />

      {/* Soft vignette to keep content readable */}
      <div
        className="absolute inset-0 z-20 pointer-events-none"
        style={{
          background:
            'radial-gradient(1200px 800px at 50% 40%, rgba(0,0,0,0) 0%, rgba(0,0,0,0.10) 70%, rgba(0,0,0,0.18) 100%)',
          opacity: 0.55,
        }}
      />
    </div>
  );
};

export default LivingBackground;
