import { WebGLRenderer, Scene, WebGLRenderTarget, PlaneGeometry, OrthographicCamera, ShaderMaterial, RGBAFormat, FloatType, LinearFilter, Mesh, EquirectangularReflectionMapping, RepeatWrapping, LinearMipMapLinearFilter, Texture, WebGLUtils } from 'three';
import { getParam } from './utils.js';

const debug = getParam("debugmaterialx");

/**
 * Renders a PMREM environment map to an equirectangular texture with specified roughness
 * @param {WebGLRenderer} renderer - Three.js WebGL renderer
 * @param {Texture} pmremTexture - PMREM texture (2D CubeUV layout) to convert
 * @param {number} roughness - Roughness value (0.0 to 1.0)
 * @param {number} width - Output texture width (default: 1024)
 * @param {number} height - Output texture height (default: 512)
 * @param {number} renderTargetHeight - Original render target height (optional, for proper PMREM parameter calculation)
 * @returns {WebGLRenderTarget} Render target containing the equirectangular texture
 * @example // Creating an equirectangular texture from a PMREM environment map at a certain roughness level:
 * const pmremRenderTarget = pmremGenerator.fromEquirectangular(envMap);
 * const equirectRenderTarget = await renderPMREMToEquirect(renderer, pmremRenderTarget.texture, 0.5, 2048, 1024, pmremRenderTarget.height);
    
    // Use the rendered equirectangular texture
    const equirectTexture = equirectRenderTarget.texture;
    
    // Apply to your material or save/export
    someMaterial.map = equirectTexture;
    
    // Don't forget to dispose when done
    // equirectRenderTarget.dispose();
 */
export function renderPMREMToEquirect(renderer: WebGLRenderer, pmremTexture: Texture, roughness = 0.0, width = 1024, height = 512, renderTargetHeight?: number) {
    // TODO Validate inputs
    // console.log(renderer, pmremTexture);

    // Calculate PMREM parameters
    // For PMREM CubeUV layout, we need the cube face size to calculate proper parameters
    // Use renderTargetHeight if provided, otherwise try to derive from texture
    let imageHeight;
    if (renderTargetHeight) {
        imageHeight = renderTargetHeight;
    } else if (pmremTexture.image) {
        imageHeight = pmremTexture.image.height / 4; // Fallback: assume CubeUV layout height / 4
    } else {
        imageHeight = 256; // Final fallback
    }

    const maxMip = Math.log2(imageHeight) - 2;
    const cubeUVHeight = imageHeight;
    const cubeUVWidth = 3 * Math.max(Math.pow(2, maxMip), 7 * 16);

    // Create render target for equirectangular output
    const renderTarget = new WebGLRenderTarget(width, height, {
        format: RGBAFormat,
        type: FloatType,
        minFilter: LinearMipMapLinearFilter,
        magFilter: LinearFilter,
        generateMipmaps: true,
        wrapS: RepeatWrapping,
        anisotropy: renderer.capabilities.getMaxAnisotropy(),
    });

    // Create fullscreen quad geometry and camera
    const geometry = new PlaneGeometry(2, 2);
    const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);

    // Create shader material for PMREM to equirectangular conversion
    const material = new ShaderMaterial({
        defines: {
            USE_ENVMAP: '',
            ENVMAP_TYPE_CUBE_UV: '',
            CUBEUV_TEXEL_WIDTH: 1.0 / cubeUVWidth,
            CUBEUV_TEXEL_HEIGHT: 1.0 / cubeUVHeight,
            CUBEUV_MAX_MIP: (maxMip + 0) + '.0',
        },
        uniforms: {
            envMap: { value: pmremTexture },
            roughness: { value: roughness }
        },
        vertexShader: `
            varying vec2 vUv;
            
            void main() {
                vUv = uv;
                gl_Position = vec4(position.xy, 0.0, 1.0);
            }
        `,
        fragmentShader: `
            uniform sampler2D envMap;
            uniform float roughness;
            varying vec2 vUv;
            
            #include <common>
            #include <cube_uv_reflection_fragment>
            
            void main() {
                // Convert UV coordinates to equirectangular direction
                vec2 uv = vUv;
                
                // Map UV (0,1) to spherical coordinates
                // Longitude: -π to π, Latitude: 0 to π
                float phi = uv.x * 2.0 * PI - PI; // Longitude (-π to π)
                float theta = uv.y * PI;          // Latitude (0 to π)
                // Rotate 90° around Y
                phi -= PI / 2.0; // Adjust to match Three.js convention

                // Convert spherical to cartesian coordinates
                vec3 direction = vec3(
                    sin(theta) * cos(phi),  // x
                    cos(theta),             // y  
                    sin(theta) * sin(phi)   // z
                );
                
                // Sample the PMREM cube texture using the direction and roughness
                #ifdef ENVMAP_TYPE_CUBE_UV
                    vec4 envColor = textureCubeUV(envMap, direction, roughness);
                #else
                    vec4 envColor = vec4(1.0, 0.0, 1.0, 1.0); // Magenta fallback
                #endif

                gl_FragColor = vec4(envColor.rgb, 1.0);
            }
        `
    });

    // Create temporary scene and mesh for rendering
    const tempScene = new Scene();
    const mesh = new Mesh(geometry, material);
    tempScene.add(mesh);

    // Store current renderer state
    const currentRenderTarget = renderer.getRenderTarget();
    const currentAutoClear = renderer.autoClear;
    const currentXrEnabled = renderer.xr.enabled;
    const currentShadowMapEnabled = renderer.shadowMap.enabled;

    renderTarget.texture.generateMipmaps = true;

    try {
        // Disable XR and shadow mapping during our render to avoid interference
        renderer.xr.enabled = false;
        renderer.shadowMap.enabled = false;

        // Render to our target
        renderer.autoClear = true;
        renderer.setRenderTarget(renderTarget);
        renderer.clear(); // Explicitly clear the render target
        renderer.render(tempScene, camera);
    } finally {
        // Restore renderer state completely
        renderer.setRenderTarget(currentRenderTarget);
        renderer.autoClear = currentAutoClear;
        renderer.xr.enabled = currentXrEnabled;
        renderer.shadowMap.enabled = currentShadowMapEnabled;

        // Clean up temporary objects
        geometry.dispose();
        material.dispose();
        tempScene.remove(mesh);
    }

    renderTarget.texture.name = 'PMREM_Equirectangular_Texture_' + roughness.toFixed(2);
    renderTarget.texture.mapping = EquirectangularReflectionMapping;

    // Log mipmap infos
    if (debug) console.log('[MaterialX] PMREM to Equirect Render Target:', {
        width: renderTarget.width,
        height: renderTarget.height,
        mipmaps: renderTarget.texture.mipmaps?.length,
        roughness: roughness,
    });

    return renderTarget;
}