export function createDisplacedSphere(THREE, config, scene) {
  // Base configuration
  const radius = config.radius ?? 3.0;
  const detail = config.detail ?? 6; // Icosahedron subdivision level (6 = ~40k verts)
  const effectType = config.effectType ?? 'noise'; // noise, spiky, molten, organic, fractal
  
  // Displacement settings
  const dispStrength = config.displacement?.strength ?? 0.5;
  const dispScale = config.displacement?.scale ?? 2.0;
  const dispSpeed = config.displacement?.speed ?? 0.5;
  const dispOctaves = config.displacement?.octaves ?? 4;
  const dispPersistence = config.displacement?.persistence ?? 0.5;
  
  // Effect-specific settings
  const spikeCount = config.spikes?.count ?? 20.0;
  const spikeSharpness = config.spikes?.sharpness ?? 3.0;
  const crackWidth = config.cracks?.width ?? 0.1;
  const crackDepth = config.cracks?.depth ?? 0.3;
  const pulseSpeed = config.pulse?.speed ?? 1.0;
  const pulseStrength = config.pulse?.strength ?? 0.2;
  const fractalPower = config.fractal?.power ?? 8.0;
  const fractalIterations = config.fractal?.iterations ?? 4.0;
  
  // Material settings
  const baseColor = new THREE.Color(config.colors?.base ?? '#6688ff');
  const accentColor = new THREE.Color(config.colors?.accent ?? '#ff8866');
  const emissiveColor = new THREE.Color(config.colors?.emissive ?? '#442244');
  const emissiveIntensity = config.colors?.emissiveIntensity ?? 0.3;
  const metalness = config.material?.metalness ?? 0.7;
  const roughness = config.material?.roughness ?? 0.3;
  const envMapIntensity = config.material?.envMapIntensity ?? 1.0;
  const fresnelPower = config.material?.fresnelPower ?? 2.0;
  const rimColor = new THREE.Color(config.material?.rimColor ?? '#88aaff');
  
  // Audio reactivity
  const audioDisplace = config.audio?.displaceReact ?? 0.3;
  const audioEmissive = config.audio?.emissiveReact ?? 0.5;
  
  // Load environment map
  const envMapPath = config.material?.envMap ?? 'images/sphere-stars.jpg';
  const textureLoader = new THREE.TextureLoader();
  const envMapTexture = textureLoader.load(envMapPath);
  envMapTexture.mapping = THREE.EquirectangularReflectionMapping;
  
  // Create high-poly icosahedron
  const geometry = new THREE.IcosahedronGeometry(radius, detail);
  
  // Vertex shader with multiple displacement modes
  const vertexShader = `
    uniform float uTime;
    uniform float uDispStrength;
    uniform float uDispScale;
    uniform float uDispSpeed;
    uniform float uDispOctaves;
    uniform float uDispPersistence;
    uniform float uSpikeCount;
    uniform float uSpikeSharpness;
    uniform float uCrackWidth;
    uniform float uCrackDepth;
    uniform float uPulseSpeed;
    uniform float uPulseStrength;
    uniform float uFractalPower;
    uniform float uFractalIterations;
    uniform float uEffectType;
    uniform float uBass;
    uniform float uTreble;
    uniform float uAudioDisplace;
    
    varying vec3 vNormal;
    varying vec3 vWorldPos;
    varying vec3 vViewDir;
    varying float vDisplacement;
    varying float vNoise;
    varying vec2 vUv;
    
    // Simplex 3D noise
    vec4 permute(vec4 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
    vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
    
    float snoise(vec3 v) {
      const vec2 C = vec2(1.0/6.0, 1.0/3.0);
      const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
      
      vec3 i = floor(v + dot(v, C.yyy));
      vec3 x0 = v - i + dot(i, C.xxx);
      
      vec3 g = step(x0.yzx, x0.xyz);
      vec3 l = 1.0 - g;
      vec3 i1 = min(g.xyz, l.zxy);
      vec3 i2 = max(g.xyz, l.zxy);
      
      vec3 x1 = x0 - i1 + C.xxx;
      vec3 x2 = x0 - i2 + C.yyy;
      vec3 x3 = x0 - D.yyy;
      
      i = mod(i, 289.0);
      vec4 p = permute(permute(permute(
          i.z + vec4(0.0, i1.z, i2.z, 1.0))
        + i.y + vec4(0.0, i1.y, i2.y, 1.0))
        + i.x + vec4(0.0, i1.x, i2.x, 1.0));
        
      float n_ = 1.0/7.0;
      vec3 ns = n_ * D.wyz - D.xzx;
      
      vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
      
      vec4 x_ = floor(j * ns.z);
      vec4 y_ = floor(j - 7.0 * x_);
      
      vec4 x = x_ *ns.x + ns.yyyy;
      vec4 y = y_ *ns.x + ns.yyyy;
      vec4 h = 1.0 - abs(x) - abs(y);
      
      vec4 b0 = vec4(x.xy, y.xy);
      vec4 b1 = vec4(x.zw, y.zw);
      
      vec4 s0 = floor(b0)*2.0 + 1.0;
      vec4 s1 = floor(b1)*2.0 + 1.0;
      vec4 sh = -step(h, vec4(0.0));
      
      vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
      vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
      
      vec3 p0 = vec3(a0.xy, h.x);
      vec3 p1 = vec3(a0.zw, h.y);
      vec3 p2 = vec3(a1.xy, h.z);
      vec3 p3 = vec3(a1.zw, h.w);
      
      vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
      p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;
      
      vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
      m = m * m;
      return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
    }
    
    // FBM noise
    float fbm(vec3 p, int octaves, float persistence) {
      float value = 0.0;
      float amplitude = 1.0;
      float frequency = 1.0;
      float maxValue = 0.0;
      
      for (int i = 0; i < 8; i++) {
        if (i >= octaves) break;
        value += amplitude * snoise(p * frequency);
        maxValue += amplitude;
        amplitude *= persistence;
        frequency *= 2.0;
      }
      return value / maxValue;
    }
    
    // Voronoi for spikes
    vec2 hash2(vec2 p) {
      p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
      return fract(sin(p) * 43758.5453);
    }
    
    float voronoi(vec3 p, float cellCount) {
      vec3 n = floor(p * cellCount);
      vec3 f = fract(p * cellCount);
      
      float minDist = 1.0;
      for (int k = -1; k <= 1; k++) {
        for (int j = -1; j <= 1; j++) {
          for (int i = -1; i <= 1; i++) {
            vec3 g = vec3(float(i), float(j), float(k));
            vec3 o = vec3(hash2(n.xy + g.xy + n.z * 17.0 + g.z * 31.0), 
                         fract(sin(dot(n + g, vec3(12.9898, 78.233, 45.164))) * 43758.5453));
            vec3 r = g + o - f;
            float d = dot(r, r);
            minDist = min(minDist, d);
          }
        }
      }
      return sqrt(minDist);
    }
    
    void main() {
      vec3 pos = position;
      vec3 norm = normalize(normal);
      float t = uTime * uDispSpeed;
      
      // Audio boost
      float audioBoost = 1.0 + uBass * uAudioDisplace;
      
      float displacement = 0.0;
      float noiseVal = 0.0;
      
      // Effect type 0: Smooth noise (Liquid Mercury)
      if (uEffectType < 0.5) {
        noiseVal = fbm(pos * uDispScale + t * 0.3, int(uDispOctaves), uDispPersistence);
        displacement = noiseVal * uDispStrength * audioBoost;
        
        // Surface tension - pull toward sphere
        float tension = 0.3;
        displacement *= (1.0 - tension) + tension * (0.5 + 0.5 * sin(t * 2.0));
      }
      // Effect type 1: Spiky (Sea Urchin)
      else if (uEffectType < 1.5) {
        float vor = voronoi(norm, uSpikeCount);
        float spike = pow(1.0 - vor, uSpikeSharpness);
        
        // Animated spike length
        float phase = snoise(norm * 3.0) * 6.28;
        float pulse = 0.5 + 0.5 * sin(t * uPulseSpeed + phase);
        
        displacement = spike * uDispStrength * pulse * audioBoost;
        noiseVal = spike;
      }
      // Effect type 2: Molten Planet
      else if (uEffectType < 2.5) {
        // Terrain
        float terrain = fbm(pos * uDispScale * 0.5 + vec3(0.0, t * 0.1, 0.0), int(uDispOctaves), 0.6);
        
        // Cracks (Voronoi edges)
        float vor = voronoi(norm, 8.0);
        float cracks = smoothstep(uCrackWidth, 0.0, vor);
        
        // Terrain goes out, cracks go in
        displacement = terrain * uDispStrength * audioBoost - cracks * uCrackDepth;
        noiseVal = cracks; // Used for emission
      }
      // Effect type 3: Organic Heartbeat
      else if (uEffectType < 3.5) {
        // Heartbeat pulse
        float heartbeat = sin(t * uPulseSpeed * 6.28);
        heartbeat = heartbeat > 0.0 ? pow(heartbeat, 0.5) : -pow(-heartbeat, 2.0);
        
        // Veins
        float veins = fbm(pos * 5.0, 3, 0.7);
        veins = smoothstep(0.3, 0.5, veins);
        
        // Bulges
        float bulge = snoise(norm * 2.0 + vec3(t * 0.5, 0.0, 0.0));
        bulge = max(0.0, bulge);
        
        displacement = (heartbeat * uPulseStrength + veins * 0.1 + bulge * 0.2) * audioBoost;
        noiseVal = veins;
      }
      // Effect type 4: Fractal Bloom
      else {
        vec3 z = norm * 1.5;
        float dr = 1.0;
        float r = 0.0;
        
        for (int i = 0; i < 8; i++) {
          if (float(i) >= uFractalIterations) break;
          
          r = length(z);
          if (r > 2.0) break;
          
          float theta = acos(z.z / r) * uFractalPower;
          float phi = atan(z.y, z.x) * uFractalPower;
          
          dr = pow(r, uFractalPower - 1.0) * uFractalPower * dr + 1.0;
          
          float sinTheta = sin(theta);
          z = pow(r, uFractalPower) * vec3(sinTheta * cos(phi), sinTheta * sin(phi), cos(theta));
          z += norm * 1.5;
        }
        
        float fractalDist = 0.5 * log(r) * r / dr;
        displacement = (1.0 - fractalDist * 2.0) * uDispStrength * audioBoost;
        
        // Animate
        displacement *= 0.8 + 0.2 * sin(t + length(pos) * 3.0);
        noiseVal = fractalDist;
      }
      
      // Apply displacement along normal
      pos += norm * displacement;
      
      // Recalculate normal (approximate)
      vNormal = normalize(normalMatrix * norm);
      vWorldPos = (modelMatrix * vec4(pos, 1.0)).xyz;
      vViewDir = normalize(cameraPosition - vWorldPos);
      vDisplacement = displacement;
      vNoise = noiseVal;
      vUv = uv;
      
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `;
  
  // Fragment shader with PBR-like lighting
  const fragmentShader = `
    uniform float uTime;
    uniform vec3 uBaseColor;
    uniform vec3 uAccentColor;
    uniform vec3 uEmissiveColor;
    uniform float uEmissiveIntensity;
    uniform float uMetalness;
    uniform float uRoughness;
    uniform float uEnvMapIntensity;
    uniform float uFresnelPower;
    uniform vec3 uRimColor;
    uniform sampler2D uEnvMap;
    uniform float uEffectType;
    uniform float uBass;
    uniform float uTreble;
    uniform float uAudioEmissive;
    uniform float uCrackWidth;
    
    varying vec3 vNormal;
    varying vec3 vWorldPos;
    varying vec3 vViewDir;
    varying float vDisplacement;
    varying float vNoise;
    varying vec2 vUv;
    
    #define PI 3.14159265359
    
    vec3 sampleEnvMap(vec3 dir) {
      float phi = atan(dir.z, dir.x);
      float theta = acos(clamp(dir.y, -1.0, 1.0));
      vec2 uv = vec2((phi + PI) / (2.0 * PI), theta / PI);
      return texture2D(uEnvMap, uv).rgb;
    }
    
    void main() {
      vec3 N = normalize(vNormal);
      vec3 V = normalize(vViewDir);
      
      // Fresnel
      float fresnel = pow(1.0 - max(dot(N, V), 0.0), uFresnelPower);
      
      // Base color with displacement-based variation
      vec3 baseCol = mix(uBaseColor, uAccentColor, clamp(vDisplacement * 2.0 + 0.5, 0.0, 1.0));
      
      // Effect-specific coloring
      float emissive = 0.0;
      
      // Molten planet: emissive cracks
      if (uEffectType > 1.5 && uEffectType < 2.5) {
        emissive = vNoise * 2.0; // cracks glow
        baseCol = mix(baseCol, vec3(0.3, 0.2, 0.15), 1.0 - vNoise * 0.5); // rocky
      }
      // Organic: vein coloring
      else if (uEffectType > 2.5 && uEffectType < 3.5) {
        baseCol = mix(baseCol, baseCol * 0.6, vNoise); // darker veins
        emissive = (1.0 - vNoise) * 0.3; // flesh glows slightly
      }
      // Fractal: iteration-based coloring
      else if (uEffectType > 3.5) {
        float hue = fract(vNoise * 3.0 + uTime * 0.1);
        vec3 fractalCol = vec3(
          0.5 + 0.5 * cos(6.28 * (hue + 0.0)),
          0.5 + 0.5 * cos(6.28 * (hue + 0.33)),
          0.5 + 0.5 * cos(6.28 * (hue + 0.67))
        );
        baseCol = mix(baseCol, fractalCol, 0.5);
        emissive = smoothstep(0.3, 0.0, vNoise) * 0.5;
      }
      
      // Environment reflection
      vec3 R = reflect(-V, N);
      vec3 envColor = sampleEnvMap(R) * uEnvMapIntensity;
      
      // Simple lighting
      vec3 lightDir = normalize(vec3(1.0, 1.0, 0.5));
      float NdotL = max(dot(N, lightDir), 0.0);
      
      // Combine
      vec3 diffuse = baseCol * (0.3 + 0.7 * NdotL);
      vec3 specular = envColor * mix(0.04, 1.0, uMetalness) * (1.0 - uRoughness);
      
      vec3 color = mix(diffuse, specular, fresnel * uMetalness);
      
      // Rim light
      color += uRimColor * fresnel * 0.5;
      
      // Emissive
      float audioEmissiveBoost = 1.0 + uBass * uAudioEmissive;
      color += uEmissiveColor * (uEmissiveIntensity + emissive) * audioEmissiveBoost;
      
      gl_FragColor = vec4(color, 1.0);
    }
  `;
  
  // Create shader material
  const material = new THREE.ShaderMaterial({
    uniforms: {
      uTime: { value: 0 },
      uDispStrength: { value: dispStrength },
      uDispScale: { value: dispScale },
      uDispSpeed: { value: dispSpeed },
      uDispOctaves: { value: dispOctaves },
      uDispPersistence: { value: dispPersistence },
      uSpikeCount: { value: spikeCount },
      uSpikeSharpness: { value: spikeSharpness },
      uCrackWidth: { value: crackWidth },
      uCrackDepth: { value: crackDepth },
      uPulseSpeed: { value: pulseSpeed },
      uPulseStrength: { value: pulseStrength },
      uFractalPower: { value: fractalPower },
      uFractalIterations: { value: fractalIterations },
      uEffectType: { value: 
        effectType === 'noise' ? 0.0 :
        effectType === 'spiky' ? 1.0 :
        effectType === 'molten' ? 2.0 :
        effectType === 'organic' ? 3.0 :
        effectType === 'fractal' ? 4.0 : 0.0
      },
      uBaseColor: { value: baseColor },
      uAccentColor: { value: accentColor },
      uEmissiveColor: { value: emissiveColor },
      uEmissiveIntensity: { value: emissiveIntensity },
      uMetalness: { value: metalness },
      uRoughness: { value: roughness },
      uEnvMapIntensity: { value: envMapIntensity },
      uFresnelPower: { value: fresnelPower },
      uRimColor: { value: rimColor },
      uEnvMap: { value: envMapTexture },
      uBass: { value: 0 },
      uTreble: { value: 0 },
      uAudioDisplace: { value: audioDisplace },
      uAudioEmissive: { value: audioEmissive },
    },
    vertexShader,
    fragmentShader,
    side: THREE.DoubleSide,
  });
  
  const mesh = new THREE.Mesh(geometry, material);
  
  // Position from config
  if (config.position) {
    mesh.position.set(
      config.position.x ?? 0,
      config.position.y ?? 0,
      config.position.z ?? 0
    );
  }
  
  // Rotation from config
  if (config.rotation) {
    mesh.rotation.set(
      (config.rotation.x ?? 0) * Math.PI / 180,
      (config.rotation.y ?? 0) * Math.PI / 180,
      (config.rotation.z ?? 0) * Math.PI / 180
    );
  }
  
  scene.add(mesh);
  
  console.log(`[DisplacedSphere] Created: effect=${effectType}, detail=${detail}, vertices=${geometry.attributes.position.count}`);
  
  return mesh;
}
