export function createCoralGrowth(THREE, config, scene) {
  const maxDepth = config.maxDepth ?? 6;
  const trunkLength = config.trunkLength ?? 1.5;
  const trunkRadius = config.trunkRadius ?? 0.08;
  const branchAngleMin = (config.branchAngleMin ?? 15) * Math.PI / 180;
  const branchAngleMax = (config.branchAngleMax ?? 45) * Math.PI / 180;
  const branchLengthRatio = config.branchLengthRatio ?? 0.75;
  const branchRadiusRatio = config.branchRadiusRatio ?? 0.7;
  const branchCountMin = config.branchCountMin ?? 2;
  const branchCountMax = config.branchCountMax ?? 4;
  const randomness = config.randomness ?? 0.3;
  const polypSize = config.polypSize ?? 0.05;
  const polypDensity = config.polypDensity ?? 0.7;
  
  // Animation
  const growthSpeed = config.animation?.growthSpeed ?? 0.3;
  const swayStrength = config.animation?.swayStrength ?? 0.4;
  const swaySpeed = config.animation?.swaySpeed ?? 0.5;
  const pulseSpeed = config.animation?.pulseSpeed ?? 2.0;
  const waveSpeed = config.animation?.waveSpeed ?? 0.3;
  
  // Colors
  const colors = config.colors || {};
  const baseColor = new THREE.Color(colors.base || '#ff6688');
  const tipColor = new THREE.Color(colors.tip || '#ffaacc');
  const polypColor = new THREE.Color(colors.polyp || '#ffffff');
  const emissiveColor = new THREE.Color(colors.emissive || '#330022');
  const emissiveIntensity = colors.emissiveIntensity ?? 0.4;
  
  // Material
  const metalness = config.material?.metalness ?? 0.2;
  const roughness = config.material?.roughness ?? 0.6;
  const envMapIntensity = config.material?.envMapIntensity ?? 0.4;
  const envMapPath = config.material?.envMap || null;
  let envMapTexture = null;
  if (envMapPath) {
    const loader = new THREE.TextureLoader();
    envMapTexture = loader.load('/' + envMapPath);
    envMapTexture.mapping = THREE.EquirectangularReflectionMapping;
  }

  const coralGroup = new THREE.Group();
  
  // Seed-based random
  let seed = config.seed ?? 54321;
  function seededRandom() {
    seed = (seed * 9301 + 49297) % 233280;
    return seed / 233280;
  }
  
  const branchMatrices = [];
  const branchDepths = [];
  const polypMatrices = [];
  const polypDepths = [];
  
  function generateCoral(position, direction, length, radius, depth) {
    if (depth > maxDepth || radius < 0.003) return;
    
    const endPos = position.clone().add(direction.clone().multiplyScalar(length));
    
    // Store branch
    const matrix = new THREE.Matrix4();
    const midPoint = position.clone().add(endPos).multiplyScalar(0.5);
    const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.normalize());
    matrix.compose(midPoint, quaternion, new THREE.Vector3(radius, length / 2, radius));
    branchMatrices.push(matrix);
    branchDepths.push(depth);
    
    // Add polyps near tips
    if (depth >= maxDepth - 2 && seededRandom() < polypDensity) {
      const polypCount = Math.floor(2 + seededRandom() * 4);
      for (let i = 0; i < polypCount; i++) {
        const t = 0.5 + seededRandom() * 0.5;
        const polypPos = position.clone().lerp(endPos, t);
        const offset = new THREE.Vector3(
          (seededRandom() - 0.5) * radius * 3,
          0,
          (seededRandom() - 0.5) * radius * 3
        );
        polypPos.add(offset);
        
        const polypMatrix = new THREE.Matrix4();
        const polypScale = polypSize * (0.5 + seededRandom() * 0.5);
        polypMatrix.compose(
          polypPos,
          new THREE.Quaternion().setFromEuler(new THREE.Euler(seededRandom() * Math.PI, seededRandom() * Math.PI * 2, 0)),
          new THREE.Vector3(polypScale, polypScale, polypScale)
        );
        polypMatrices.push(polypMatrix);
        polypDepths.push(depth);
      }
    }
    
    // Generate child branches
    if (depth < maxDepth) {
      const childCount = branchCountMin + Math.floor(seededRandom() * (branchCountMax - branchCountMin + 1));
      const childLength = length * branchLengthRatio * (1 + (seededRandom() - 0.5) * randomness);
      const childRadius = radius * branchRadiusRatio;
      
      for (let i = 0; i < childCount; i++) {
        const angle = (i / childCount) * Math.PI * 2 + seededRandom() * 0.5;
        const spreadAngle = branchAngleMin + seededRandom() * (branchAngleMax - branchAngleMin);
        
        // Create direction with organic variation
        const localDir = new THREE.Vector3(
          Math.sin(spreadAngle) * Math.cos(angle),
          Math.cos(spreadAngle),
          Math.sin(spreadAngle) * Math.sin(angle)
        );
        
        // Transform to world space
        const worldQuat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction.normalize());
        localDir.applyQuaternion(worldQuat);
        
        // Add some randomness
        localDir.x += (seededRandom() - 0.5) * randomness * 0.5;
        localDir.z += (seededRandom() - 0.5) * randomness * 0.5;
        localDir.normalize();
        
        generateCoral(endPos, localDir, childLength, childRadius, depth + 1);
      }
    }
  }
  
  // Generate multiple coral branches from base
  const baseCount = config.baseCount ?? 3;
  for (let i = 0; i < baseCount; i++) {
    const angle = (i / baseCount) * Math.PI * 2;
    const startDir = new THREE.Vector3(Math.sin(angle) * 0.3, 1, Math.cos(angle) * 0.3).normalize();
    const startPos = new THREE.Vector3(Math.sin(angle) * 0.2, 0, Math.cos(angle) * 0.2);
    generateCoral(startPos, startDir, trunkLength, trunkRadius, 0);
  }
  
  console.log(`[CoralGrowth] Generated ${branchMatrices.length} branches, ${polypMatrices.length} polyps`);
  
  // Create branch mesh
  const branchGeo = new THREE.CylinderGeometry(1, 1.1, 2, 6, 1);
  
  const branchMaterial = new THREE.ShaderMaterial({
    uniforms: {
      uTime: { value: 0 },
      uGrowth: { value: 1.0 },
      uSwayStrength: { value: swayStrength },
      uSwaySpeed: { value: swaySpeed },
      uWaveSpeed: { value: waveSpeed },
      uBaseColor: { value: baseColor },
      uTipColor: { value: tipColor },
      uEmissiveColor: { value: emissiveColor },
      uEmissiveIntensity: { value: emissiveIntensity },
      uMetalness: { value: metalness },
      uRoughness: { value: roughness },
      uEnvMapIntensity: { value: envMapIntensity },
      uEnvMap: { value: envMapTexture },
      uHasEnvMap: { value: envMapTexture ? 1.0 : 0.0 },
      uMaxDepth: { value: maxDepth },
    },
    vertexShader: `
      attribute float instanceDepth;
      
      uniform float uTime;
      uniform float uGrowth;
      uniform float uSwayStrength;
      uniform float uSwaySpeed;
      uniform float uWaveSpeed;
      uniform float uMaxDepth;
      
      varying vec3 vNormal;
      varying vec3 vWorldPos;
      varying vec3 vViewDir;
      varying vec3 vReflect;
      varying float vDepth;
      varying float vGrowthFactor;
      
      void main() {
        vDepth = instanceDepth;
        vNormal = normalize(normalMatrix * normal);
        
        float depthNorm = instanceDepth / uMaxDepth;
        vGrowthFactor = smoothstep(depthNorm - 0.1, depthNorm + 0.1, uGrowth);
        
        vec3 instancePos = vec3(instanceMatrix[3][0], instanceMatrix[3][1], instanceMatrix[3][2]);
        
        float phase = uTime * uSwaySpeed + instancePos.x * 0.5 + instancePos.z * 0.3;
        float sway = sin(phase) * uSwayStrength * depthNorm;
        float wave = sin(uTime * uWaveSpeed + depthNorm * 6.28) * uSwayStrength * 0.3;
        
        vec3 scaledPos = position;
        scaledPos.y *= vGrowthFactor;
        
        vec4 worldPos = instanceMatrix * vec4(scaledPos, 1.0);
        worldPos.x += sway + wave;
        worldPos.z += sway * 0.5;
        
        vWorldPos = worldPos.xyz;
        vViewDir = normalize(cameraPosition - worldPos.xyz);
        vReflect = reflect(-vViewDir, vNormal);
        gl_Position = projectionMatrix * viewMatrix * worldPos;
      }
    `,
    fragmentShader: `
      uniform vec3 uBaseColor;
      uniform vec3 uTipColor;
      uniform vec3 uEmissiveColor;
      uniform float uEmissiveIntensity;
      uniform float uMetalness;
      uniform float uRoughness;
      uniform float uEnvMapIntensity;
      uniform sampler2D uEnvMap;
      uniform float uHasEnvMap;
      uniform float uMaxDepth;
      
      varying vec3 vNormal;
      varying vec3 vWorldPos;
      varying vec3 vViewDir;
      varying vec3 vReflect;
      varying float vDepth;
      varying float vGrowthFactor;
      
      #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() {
        if (vGrowthFactor < 0.01) discard;
        
        float depthNorm = vDepth / uMaxDepth;
        vec3 baseColor = mix(uBaseColor, uTipColor, depthNorm);
        
        vec3 normal = normalize(vNormal);
        vec3 viewDir = normalize(vViewDir);
        vec3 lightDir = normalize(vec3(0.5, 1.0, 0.5));
        vec3 halfDir = normalize(lightDir + viewDir);
        
        float fresnel = pow(1.0 - max(dot(normal, viewDir), 0.0), 3.0);
        
        float NdotL = max(dot(normal, lightDir), 0.0);
        float ambient = 0.25;
        
        float specPower = mix(256.0, 8.0, uRoughness);
        float specular = pow(max(dot(normal, halfDir), 0.0), specPower);
        
        vec3 diffuseColor = baseColor * (1.0 - uMetalness);
        vec3 specularColor = mix(vec3(0.04), baseColor, uMetalness);
        
        vec3 color = diffuseColor * (ambient + NdotL * 0.6);
        color += specularColor * specular * (1.0 - uRoughness * 0.5);
        
        // Environment map reflection
        if (uHasEnvMap > 0.5) {
          vec3 envColor = sampleEnvMap(normalize(vReflect));
          color += specularColor * envColor * fresnel * uEnvMapIntensity;
        } else {
          color += specularColor * fresnel * uEnvMapIntensity;
        }
        
        color += uEmissiveColor * uEmissiveIntensity;
        
        gl_FragColor = vec4(color, 1.0);
      }
    `,
    side: THREE.DoubleSide,
  });
  
  const branchMesh = new THREE.InstancedMesh(branchGeo, branchMaterial, branchMatrices.length);
  const depthArray = new Float32Array(branchMatrices.length);
  for (let i = 0; i < branchMatrices.length; i++) {
    branchMesh.setMatrixAt(i, branchMatrices[i]);
    depthArray[i] = branchDepths[i];
  }
  branchMesh.instanceMatrix.needsUpdate = true;
  branchGeo.setAttribute('instanceDepth', new THREE.InstancedBufferAttribute(depthArray, 1));
  coralGroup.add(branchMesh);
  
  // Create polyps if any
  if (polypMatrices.length > 0) {
    const polypGeo = new THREE.SphereGeometry(1, 8, 6);
    const polypMaterial = new THREE.ShaderMaterial({
      uniforms: {
        uTime: { value: 0 },
        uGrowth: { value: 1.0 },
        uPulseSpeed: { value: pulseSpeed },
        uPolypColor: { value: polypColor },
        uEmissiveColor: { value: emissiveColor },
        uEmissiveIntensity: { value: emissiveIntensity },
        uMaxDepth: { value: maxDepth },
      },
      vertexShader: `
        attribute float instanceDepth;
        
        uniform float uTime;
        uniform float uGrowth;
        uniform float uPulseSpeed;
        uniform float uMaxDepth;
        
        varying vec3 vNormal;
        varying float vPulse;
        varying float vGrowthFactor;
        
        void main() {
          vNormal = normalize(normalMatrix * normal);
          
          float depthNorm = instanceDepth / uMaxDepth;
          vGrowthFactor = smoothstep(0.7 + depthNorm * 0.3, 0.8 + depthNorm * 0.2, uGrowth);
          
          vec3 instancePos = vec3(instanceMatrix[3][0], instanceMatrix[3][1], instanceMatrix[3][2]);
          
          // Pulsing animation
          float pulse = sin(uTime * uPulseSpeed + instancePos.x * 5.0 + instancePos.z * 5.0);
          vPulse = pulse * 0.5 + 0.5;
          
          float scale = 1.0 + pulse * 0.2;
          vec3 scaledPos = position * scale * vGrowthFactor;
          
          vec4 worldPos = instanceMatrix * vec4(scaledPos, 1.0);
          gl_Position = projectionMatrix * viewMatrix * worldPos;
        }
      `,
      fragmentShader: `
        uniform vec3 uPolypColor;
        uniform vec3 uEmissiveColor;
        uniform float uEmissiveIntensity;
        
        varying vec3 vNormal;
        varying float vPulse;
        varying float vGrowthFactor;
        
        void main() {
          if (vGrowthFactor < 0.01) discard;
          
          vec3 lightDir = normalize(vec3(0.5, 1.0, 0.5));
          float diffuse = max(dot(vNormal, lightDir), 0.0);
          
          vec3 color = uPolypColor * (0.5 + diffuse * 0.5);
          color += uEmissiveColor * uEmissiveIntensity * (0.5 + vPulse * 0.5);
          
          gl_FragColor = vec4(color, 0.9);
        }
      `,
      transparent: true,
      side: THREE.DoubleSide,
    });
    
    const polypMesh = new THREE.InstancedMesh(polypGeo, polypMaterial, polypMatrices.length);
    const polypDepthArray = new Float32Array(polypMatrices.length);
    for (let i = 0; i < polypMatrices.length; i++) {
      polypMesh.setMatrixAt(i, polypMatrices[i]);
      polypDepthArray[i] = polypDepths[i];
    }
    polypMesh.instanceMatrix.needsUpdate = true;
    polypGeo.setAttribute('instanceDepth', new THREE.InstancedBufferAttribute(polypDepthArray, 1));
    coralGroup.add(polypMesh);
    coralGroup.userData.polypMesh = polypMesh;
  }
  
  coralGroup.userData.branchMesh = branchMesh;
  coralGroup.userData.config = config;
  
  if (config.position) {
    coralGroup.position.set(config.position.x ?? 0, config.position.y ?? 0, config.position.z ?? 0);
  }
  if (config.rotation) {
    coralGroup.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(coralGroup);
  return coralGroup;
}
