export function createMengerSponge(THREE, config, scene) {
  const maxDepth = config.maxDepth ?? 3;
  const baseSize = config.baseSize ?? 4.0;
  const gap = config.gap ?? 0.02;
  const assemblyProgress = config.animation?.assemblyProgress ?? 1.0;
  const rotationSpeed = config.animation?.rotationSpeed ?? 0.1;
  const breathAmount = config.animation?.breathAmount ?? 0.0;
  const breathSpeed = config.animation?.breathSpeed ?? 1.0;
  
  // Colors
  const colors = config.colors || {};
  const innerColor = new THREE.Color(colors.inner || '#ff6600');
  const outerColor = new THREE.Color(colors.outer || '#0066ff');
  const emissiveColor = new THREE.Color(colors.emissive || '#331100');
  const emissiveIntensity = colors.emissiveIntensity ?? 0.3;
  
  // Material
  const metalness = config.material?.metalness ?? 0.7;
  const roughness = config.material?.roughness ?? 0.3;
  const envMapIntensity = config.material?.envMapIntensity ?? 0.6;
  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 spongeGroup = new THREE.Group();
  
  // Collect cube transforms
  const cubeMatrices = [];
  const cubeDepths = [];
  const cubeDistances = []; // Distance from center for assembly animation
  
  function shouldRemove(x, y, z) {
    // Remove center cube and face centers
    let count = 0;
    if (x === 1) count++;
    if (y === 1) count++;
    if (z === 1) count++;
    return count >= 2;
  }
  
  function generateMenger(center, size, depth) {
    if (depth > maxDepth) return;
    
    if (depth === maxDepth) {
      const matrix = new THREE.Matrix4();
      matrix.compose(
        center,
        new THREE.Quaternion(),
        new THREE.Vector3(size * (1 - gap), size * (1 - gap), size * (1 - gap))
      );
      cubeMatrices.push(matrix);
      cubeDepths.push(depth);
      cubeDistances.push(center.length());
      return;
    }
    
    const newSize = size / 3;
    for (let x = 0; x < 3; x++) {
      for (let y = 0; y < 3; y++) {
        for (let z = 0; z < 3; z++) {
          if (shouldRemove(x, y, z)) continue;
          
          const offset = new THREE.Vector3(
            (x - 1) * newSize,
            (y - 1) * newSize,
            (z - 1) * newSize
          );
          const newCenter = center.clone().add(offset);
          generateMenger(newCenter, newSize, depth + 1);
        }
      }
    }
  }
  
  generateMenger(new THREE.Vector3(0, 0, 0), baseSize, 0);
  
  // Normalize distances for animation
  const maxDist = Math.max(...cubeDistances, 0.001);
  for (let i = 0; i < cubeDistances.length; i++) {
    cubeDistances[i] /= maxDist;
  }
  
  console.log(`[MengerSponge] Generated ${cubeMatrices.length} cubes at depth ${maxDepth}`);
  
  const cubeGeo = new THREE.BoxGeometry(1, 1, 1);
  
  const cubeMaterial = new THREE.ShaderMaterial({
    uniforms: {
      uTime: { value: 0 },
      uAssembly: { value: assemblyProgress },
      uRotationSpeed: { value: rotationSpeed },
      uBreathAmount: { value: breathAmount },
      uBreathSpeed: { value: breathSpeed },
      uInnerColor: { value: innerColor },
      uOuterColor: { value: outerColor },
      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;
      attribute float instanceDistance;
      
      uniform float uTime;
      uniform float uAssembly;
      uniform float uRotationSpeed;
      uniform float uBreathAmount;
      uniform float uBreathSpeed;
      uniform float uMaxDepth;
      
      varying vec3 vNormal;
      varying vec3 vWorldPos;
      varying vec3 vViewDir;
      varying vec3 vReflect;
      varying float vDepth;
      varying float vAssemblyFactor;
      
      mat3 rotateY(float angle) {
        float c = cos(angle);
        float s = sin(angle);
        return mat3(c, 0, s, 0, 1, 0, -s, 0, c);
      }
      
      void main() {
        vDepth = instanceDepth;
        
        vec3 instancePos = vec3(instanceMatrix[3][0], instanceMatrix[3][1], instanceMatrix[3][2]);
        float instanceScale = length(vec3(instanceMatrix[0][0], instanceMatrix[1][0], instanceMatrix[2][0]));
        
        float assemblyThreshold = instanceDistance;
        vAssemblyFactor = smoothstep(assemblyThreshold - 0.1, assemblyThreshold + 0.1, uAssembly);
        
        vec3 flyDir = normalize(instancePos + vec3(0.001));
        float flyDist = (1.0 - vAssemblyFactor) * 10.0;
        vec3 animatedPos = instancePos + flyDir * flyDist;
        
        float breath = 1.0 + sin(uTime * uBreathSpeed) * uBreathAmount;
        animatedPos *= breath;
        
        float rotAngle = uTime * uRotationSpeed;
        mat3 rot = rotateY(rotAngle);
        animatedPos = rot * animatedPos;
        
        vec3 worldPos = animatedPos + position * instanceScale * vAssemblyFactor;
        
        vWorldPos = worldPos;
        vViewDir = normalize(cameraPosition - worldPos);
        vNormal = normalize(rot * normal);
        vReflect = reflect(-vViewDir, vNormal);
        
        gl_Position = projectionMatrix * viewMatrix * vec4(worldPos, 1.0);
      }
    `,
    fragmentShader: `
      uniform vec3 uInnerColor;
      uniform vec3 uOuterColor;
      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 vAssemblyFactor;
      
      #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 (vAssemblyFactor < 0.01) discard;
        
        float depthNorm = vDepth / uMaxDepth;
        vec3 baseColor = mix(uOuterColor, uInnerColor, depthNorm);
        
        vec3 normal = normalize(vNormal);
        vec3 viewDir = normalize(vViewDir);
        vec3 lightDir = normalize(vec3(1.0, 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.2;
        
        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.FrontSide,
  });
  
  const cubeMesh = new THREE.InstancedMesh(cubeGeo, cubeMaterial, cubeMatrices.length);
  
  const depthArray = new Float32Array(cubeMatrices.length);
  const distArray = new Float32Array(cubeMatrices.length);
  for (let i = 0; i < cubeMatrices.length; i++) {
    cubeMesh.setMatrixAt(i, cubeMatrices[i]);
    depthArray[i] = cubeDepths[i];
    distArray[i] = cubeDistances[i];
  }
  cubeMesh.instanceMatrix.needsUpdate = true;
  cubeGeo.setAttribute('instanceDepth', new THREE.InstancedBufferAttribute(depthArray, 1));
  cubeGeo.setAttribute('instanceDistance', new THREE.InstancedBufferAttribute(distArray, 1));
  
  spongeGroup.add(cubeMesh);
  spongeGroup.userData.cubeMesh = cubeMesh;
  spongeGroup.userData.config = config;
  
  if (config.position) {
    spongeGroup.position.set(config.position.x ?? 0, config.position.y ?? 0, config.position.z ?? 0);
  }
  if (config.rotation) {
    spongeGroup.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(spongeGroup);
  return spongeGroup;
}
