export function createFractalTree(THREE, config, scene) {
  // Tree structure parameters
  const maxDepth = config.maxDepth ?? 7;
  const trunkHeight = config.trunkHeight ?? 2.0;
  const trunkRadius = config.trunkRadius ?? 0.15;
  const branchAngle = (config.branchAngle ?? 30) * Math.PI / 180;
  const branchLengthRatio = config.branchLengthRatio ?? 0.7;
  const branchRadiusRatio = config.branchRadiusRatio ?? 0.65;
  const branchCount = Math.max(2, Math.min(5, config.branchCount ?? 3));
  const twist = (config.twist ?? 45) * Math.PI / 180;
  const randomness = config.randomness ?? 0.15;
  const leafSize = config.leafSize ?? 0.08;
  const leafDensity = config.leafDensity ?? 0.5;
  
  // Animation parameters
  const growthSpeed = config.animation?.growthSpeed ?? 0.5;
  const windStrength = config.animation?.windStrength ?? 0.3;
  const windSpeed = config.animation?.windSpeed ?? 1.0;
  const swayAmount = config.animation?.swayAmount ?? 0.2;
  
  // Colors
  const trunkColor = new THREE.Color(config.colors?.trunk || '#4a3728');
  const branchColor = new THREE.Color(config.colors?.branch || '#5d4a3a');
  const leafColorStart = new THREE.Color(config.colors?.leafStart || '#228b22');
  const leafColorEnd = new THREE.Color(config.colors?.leafEnd || '#90ee90');
  const emissiveColor = new THREE.Color(config.colors?.emissive || '#000000');
  const emissiveIntensity = config.colors?.emissiveIntensity ?? 0.0;
  
  // Material settings
  const metalness = config.material?.metalness ?? 0.1;
  const roughness = config.material?.roughness ?? 0.8;
  
  // Create a container group for the whole tree
  const treeGroup = new THREE.Group();
  
  // Arrays to collect all branch segments for instancing
  const branchMatrices = [];
  const branchDepths = [];
  const leafMatrices = [];
  const leafDepths = [];
  
  // Seed-based random for reproducibility
  let seed = config.seed ?? 12345;
  function seededRandom() {
    seed = (seed * 9301 + 49297) % 233280;
    return seed / 233280;
  }
  
  // Recursive branch generation (collect transforms, don't create objects yet)
  function generateBranch(position, direction, length, radius, depth, parentAngle) {
    if (depth > maxDepth || radius < 0.005) return;
    
    // Calculate end position
    const endPos = position.clone().add(direction.clone().multiplyScalar(length));
    
    // Store branch transform
    const matrix = new THREE.Matrix4();
    const midPoint = position.clone().add(endPos).multiplyScalar(0.5);
    
    // Create rotation from direction
    const up = new THREE.Vector3(0, 1, 0);
    const quaternion = new THREE.Quaternion();
    quaternion.setFromUnitVectors(up, direction.clone().normalize());
    
    matrix.compose(
      midPoint,
      quaternion,
      new THREE.Vector3(radius, length / 2, radius)
    );
    
    branchMatrices.push(matrix);
    branchDepths.push(depth);
    
    // Add leaves at branch tips (higher depth = more leaves)
    if (depth >= maxDepth - 2) {
      const leafCount = Math.floor(leafDensity * (3 + seededRandom() * 5));
      for (let i = 0; i < leafCount; i++) {
        const leafOffset = new THREE.Vector3(
          (seededRandom() - 0.5) * length * 0.5,
          seededRandom() * length * 0.3,
          (seededRandom() - 0.5) * length * 0.5
        );
        const leafPos = endPos.clone().add(leafOffset);
        
        const leafMatrix = new THREE.Matrix4();
        const leafScale = leafSize * (0.5 + seededRandom() * 0.5);
        const leafRotation = new THREE.Quaternion();
        leafRotation.setFromEuler(new THREE.Euler(
          seededRandom() * Math.PI,
          seededRandom() * Math.PI * 2,
          seededRandom() * Math.PI
        ));
        
        leafMatrix.compose(
          leafPos,
          leafRotation,
          new THREE.Vector3(leafScale, leafScale * 0.5, leafScale)
        );
        
        leafMatrices.push(leafMatrix);
        leafDepths.push(depth);
      }
    }
    
    // Generate child branches
    if (depth < maxDepth) {
      const childLength = length * branchLengthRatio * (1 + (seededRandom() - 0.5) * randomness);
      const childRadius = radius * branchRadiusRatio;
      
      for (let i = 0; i < branchCount; i++) {
        // Distribute branches around the parent
        const angle = (i / branchCount) * Math.PI * 2 + parentAngle + twist;
        const spreadAngle = branchAngle * (1 + (seededRandom() - 0.5) * randomness);
        
        // Create new direction
        const newDirection = direction.clone();
        
        // Rotate around local Y (twist)
        const rotY = new THREE.Matrix4().makeRotationY(angle);
        // Rotate away from parent (spread)
        const rotSpread = new THREE.Matrix4().makeRotationX(spreadAngle);
        
        // Apply rotations
        const localDir = new THREE.Vector3(0, 1, 0);
        localDir.applyMatrix4(rotSpread);
        localDir.applyMatrix4(rotY);
        
        // Transform to world space based on parent direction
        const worldQuat = new THREE.Quaternion().setFromUnitVectors(
          new THREE.Vector3(0, 1, 0),
          direction.normalize()
        );
        localDir.applyQuaternion(worldQuat);
        
        generateBranch(endPos, localDir.normalize(), childLength, childRadius, depth + 1, angle);
      }
    }
  }
  
  // Generate the tree structure
  const startPos = new THREE.Vector3(0, 0, 0);
  const startDir = new THREE.Vector3(0, 1, 0);
  generateBranch(startPos, startDir, trunkHeight, trunkRadius, 0, 0);
  
  console.log(`[FractalTree] Generated ${branchMatrices.length} branches, ${leafMatrices.length} leaves`);
  
  // Create instanced geometry for branches
  const branchGeometry = new THREE.CylinderGeometry(1, 1, 2, 8, 1);
  
  // Environment map
  const envMapIntensity = config.material?.envMapIntensity ?? 0.3;
  const envMapPath = config.material?.envMap || null;
  let envMapTexture = null;
  if (envMapPath) {
    const loader = new THREE.TextureLoader();
    envMapTexture = loader.load('/' + envMapPath);
    envMapTexture.mapping = THREE.EquirectangularReflectionMapping;
  }
  
  // Create shader material for branches with wind animation
  const branchMaterial = new THREE.ShaderMaterial({
    uniforms: {
      uTime: { value: 0 },
      uGrowth: { value: 1.0 },
      uWindStrength: { value: windStrength },
      uWindSpeed: { value: windSpeed },
      uSwayAmount: { value: swayAmount },
      uTrunkColor: { value: trunkColor },
      uBranchColor: { value: branchColor },
      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 uWindStrength;
      uniform float uWindSpeed;
      uniform float uSwayAmount;
      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;
        float growthThreshold = depthNorm;
        vGrowthFactor = smoothstep(growthThreshold - 0.1, growthThreshold + 0.1, uGrowth);
        
        vec4 worldPos = instanceMatrix * vec4(position, 1.0);
        
        float windPhase = uTime * uWindSpeed + worldPos.x * 0.5 + worldPos.z * 0.3;
        float windEffect = sin(windPhase) * uWindStrength * depthNorm;
        float swayEffect = sin(uTime * uWindSpeed * 0.7 + instanceDepth) * uSwayAmount * depthNorm;
        
        worldPos.x += windEffect + swayEffect;
        worldPos.z += windEffect * 0.5;
        
        vec3 scaledPos = position;
        scaledPos.y *= vGrowthFactor;
        
        vec4 finalPos = instanceMatrix * vec4(scaledPos, 1.0);
        finalPos.x += windEffect + swayEffect;
        finalPos.z += windEffect * 0.5;
        
        vWorldPos = finalPos.xyz;
        vViewDir = normalize(cameraPosition - finalPos.xyz);
        vReflect = reflect(-vViewDir, vNormal);
        gl_Position = projectionMatrix * viewMatrix * finalPos;
      }
    `,
    fragmentShader: `
      uniform vec3 uTrunkColor;
      uniform vec3 uBranchColor;
      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 depthMix = vDepth / uMaxDepth;
        vec3 baseColor = mix(uTrunkColor, uBranchColor, depthMix);
        
        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.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);
        
        // 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,
  });
  
  // Create instanced mesh for branches
  const branchMesh = new THREE.InstancedMesh(branchGeometry, branchMaterial, branchMatrices.length);
  
  // Set instance matrices and depth attribute
  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;
  branchGeometry.setAttribute('instanceDepth', new THREE.InstancedBufferAttribute(depthArray, 1));
  
  treeGroup.add(branchMesh);
  
  // Create leaves if we have any
  if (leafMatrices.length > 0) {
    const leafGeometry = new THREE.SphereGeometry(1, 6, 4);
    
    const leafMaterial = new THREE.ShaderMaterial({
      uniforms: {
        uTime: { value: 0 },
        uGrowth: { value: 1.0 },
        uWindStrength: { value: windStrength * 1.5 },
        uWindSpeed: { value: windSpeed },
        uLeafColorStart: { value: leafColorStart },
        uLeafColorEnd: { value: leafColorEnd },
        uEmissiveColor: { value: emissiveColor },
        uEmissiveIntensity: { value: emissiveIntensity },
        uMaxDepth: { value: maxDepth },
      },
      vertexShader: `
        attribute float instanceDepth;
        
        uniform float uTime;
        uniform float uGrowth;
        uniform float uWindStrength;
        uniform float uWindSpeed;
        uniform float uMaxDepth;
        
        varying vec3 vNormal;
        varying float vColorMix;
        varying float vGrowthFactor;
        
        void main() {
          vNormal = normalize(normalMatrix * normal);
          
          float depthNorm = instanceDepth / uMaxDepth;
          vColorMix = depthNorm + sin(uTime * 0.5) * 0.2;
          
          // Leaves appear later in growth
          float growthThreshold = 0.7 + depthNorm * 0.3;
          vGrowthFactor = smoothstep(growthThreshold - 0.1, growthThreshold + 0.1, uGrowth);
          
          vec4 worldPos = instanceMatrix * vec4(position, 1.0);
          
          // Leaf flutter animation
          float flutter = sin(uTime * uWindSpeed * 3.0 + worldPos.x * 2.0 + worldPos.z * 2.0);
          worldPos.x += flutter * uWindStrength * 0.3;
          worldPos.y += abs(flutter) * uWindStrength * 0.1;
          worldPos.z += flutter * uWindStrength * 0.2;
          
          // Scale by growth
          vec3 scaledPos = position * vGrowthFactor;
          vec4 finalPos = instanceMatrix * vec4(scaledPos, 1.0);
          finalPos.xyz += worldPos.xyz - (instanceMatrix * vec4(position, 1.0)).xyz;
          
          gl_Position = projectionMatrix * viewMatrix * finalPos;
        }
      `,
      fragmentShader: `
        uniform vec3 uLeafColorStart;
        uniform vec3 uLeafColorEnd;
        uniform vec3 uEmissiveColor;
        uniform float uEmissiveIntensity;
        
        varying vec3 vNormal;
        varying float vColorMix;
        varying float vGrowthFactor;
        
        void main() {
          if (vGrowthFactor < 0.01) discard;
          
          vec3 leafColor = mix(uLeafColorStart, uLeafColorEnd, clamp(vColorMix, 0.0, 1.0));
          
          vec3 lightDir = normalize(vec3(1.0, 1.0, 0.5));
          float diffuse = max(dot(vNormal, lightDir), 0.0);
          float ambient = 0.4;
          
          vec3 color = leafColor * (ambient + diffuse * 0.6);
          color += uEmissiveColor * uEmissiveIntensity;
          
          gl_FragColor = vec4(color, 0.9);
        }
      `,
      side: THREE.DoubleSide,
      transparent: true,
    });
    
    const leafMesh = new THREE.InstancedMesh(leafGeometry, leafMaterial, leafMatrices.length);
    
    const leafDepthArray = new Float32Array(leafMatrices.length);
    for (let i = 0; i < leafMatrices.length; i++) {
      leafMesh.setMatrixAt(i, leafMatrices[i]);
      leafDepthArray[i] = leafDepths[i];
    }
    leafMesh.instanceMatrix.needsUpdate = true;
    leafGeometry.setAttribute('instanceDepth', new THREE.InstancedBufferAttribute(leafDepthArray, 1));
    
    treeGroup.add(leafMesh);
    treeGroup.userData.leafMesh = leafMesh;
  }
  
  // Store references for animation
  treeGroup.userData.branchMesh = branchMesh;
  treeGroup.userData.config = config;
  
  // Position the tree
  if (config.position) {
    treeGroup.position.set(
      config.position.x ?? 0,
      config.position.y ?? 0,
      config.position.z ?? 0
    );
  }
  
  // Rotation from config
  if (config.rotation) {
    treeGroup.rotation.set(
      (config.rotation.x ?? 0) * Math.PI / 180,
      (config.rotation.y ?? 0) * Math.PI / 180,
      (config.rotation.z ?? 0) * Math.PI / 180
    );
  }
  
  // Scale from config
  if (config.scale) {
    const s = typeof config.scale === 'number' ? config.scale : 1;
    treeGroup.scale.set(
      config.scale.x ?? s,
      config.scale.y ?? s,
      config.scale.z ?? s
    );
  }
  
  scene.add(treeGroup);
  
  return treeGroup;
}
