import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
import { RoundedBoxGeometry } from 'RoundedBoxGeometry';
import { TextGeometry } from 'TextGeometry';
import { GLTFLoader } from 'GLTFLoader';
import { FontLoader } from 'FontLoader';

let scene, camera, renderer, controls;

function init() {
    scene = new THREE.Scene();
    setupCamera();
    setupRenderer();
    setupControls();
    addLighting();
    loadModel('./js/models/net.gltf', () => {
        addCourtAndLines();
        window.addEventListener('resize', onWindowResize);
        animate();
    });
}

function setupCamera() {
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 6000);
    camera.position.set(2250, 0, 0);
}

function setupRenderer() {
    renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('court'), antialias: true, alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.outputEncoding = THREE.sRGBEncoding;
    document.body.appendChild(renderer.domElement);
}

function setupControls() {
    controls = new OrbitControls(camera, renderer.domElement);
    controls.target.set(0, 250, 0);
    controls.maxPolarAngle = Math.PI / 2.5;
    controls.minPolarAngle = Math.PI / 2.5;
    controls.minDistance = 2250;
    controls.maxDistance = 2250;
    controls.enablePan = false;
    controls.autoRotate = true;
    controls.autoRotateSpeed = -0.25;
    controls.enableDamping = true;
    controls.dampingFactor = 0.1;
}
function addLighting() {
    const ambientLight = new THREE.AmbientLight(0x404040);
    scene.add(ambientLight);

    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(5, 5, 5).normalize();
    scene.add(directionalLight);
}

function loadModel(path, onLoadCallback) {
    const loader = new GLTFLoader();
    loader.load(path, function (gltf) {
        // console.log('Model loaded:', gltf);
        const model = gltf.scene;

        model.traverse((node) => {
            if (node.isMesh) {
                node.material = new THREE.MeshBasicMaterial({ color: 0x000000 });
            }
        });

        model.scale.set(100, 120, 100);
        model.position.set(0, 0, 0);
        scene.add(model);
        onLoadCallback();
    }, undefined, function (error) {
        console.error('Error loading GLTF model:', error);
    });
}

function addCourtAndLines() {
    const materials = [
        new THREE.MeshBasicMaterial({ color: 0x1a4b9f }),
        new THREE.MeshBasicMaterial({ color: 0x1a4b9f }),
        new THREE.MeshBasicMaterial({ color: 0x2261CE }),
        new THREE.MeshBasicMaterial({ color: 0x1a4b9f }),
        new THREE.MeshBasicMaterial({ color: 0x1a4b9f }),
        new THREE.MeshBasicMaterial({ color: 0x1a4b9f })
    ];
    const court = new THREE.Mesh(new RoundedBoxGeometry(2000, 100, 1000), materials);
    court.position.set(0, -20, 0);
    scene.add(court);

    const lineMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
    const positions = [0, 750, -750];
    positions.forEach(pos => {
        const line = createMesh(new RoundedBoxGeometry(10, 10, 1000), lineMaterial, new THREE.Vector3(pos, 30, 0));
        scene.add(line);
    });

    const centerLine = createMesh(new RoundedBoxGeometry(1500, 10, 20), lineMaterial, new THREE.Vector3(0, 30, 0));
    scene.add(centerLine);
    const shape = new THREE.Shape();
    shape.moveTo(0, 0);
    shape.lineTo(0, 110);
    shape.lineTo(225, 0);
    shape.lineTo(0, 0);

    const extrudeSettings = {
        depth: 1000,
        bevelEnabled: false,
    };
    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
    const material = new THREE.MeshBasicMaterial({ color: 0x3B3B3B });
    const ramp = new THREE.Mesh(geometry, material);
    ramp.position.set(1025, -55, -500);
    scene.add(ramp);

    const ramp2 = new THREE.Mesh(geometry, material);
    ramp2.position.set(-1025, -55, 500);
    ramp2.rotateY(180 * Math.PI / 180);
    scene.add(ramp2);
}

function createAds(adsData) {
    const roundedBoxGeometry = new RoundedBoxGeometry(950, 1, 200, 10);

    let saturation = 1.0;

    const materialAds = new THREE.ShaderMaterial({
        uniforms: {
            uTexture: { value: null },
            uSaturation: { value: saturation }
        },
        vertexShader: `
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
        `,
        fragmentShader: `
        uniform sampler2D uTexture;
        uniform float uSaturation;
        varying vec2 vUv;
        vec3 adjustSaturation(vec3 color, float saturation) {
            vec3 gray = vec3(dot(color, vec3(0.299, 0.587, 0.114)));
            return mix(gray, color, saturation);
        }
        void main() {
            vec4 texColor = texture2D(uTexture, vUv);
            texColor.rgb = adjustSaturation(texColor.rgb, uSaturation);
            gl_FragColor = texColor;
        }
        `,
        transparent: true,
        precision: 'highp'
    });

    const ads = new THREE.Mesh(roundedBoxGeometry, materialAds);
    ads.position.set(1140, 0, 0);
    ads.rotateZ(-26.5 * Math.PI / 180);
    ads.rotateY(90 * Math.PI / 180);
    scene.add(ads);

    const ads2 = new THREE.Mesh(roundedBoxGeometry, materialAds);
    ads2.position.set(-1140, 0, 0);
    ads2.rotateY(-90 * Math.PI / 180);
    ads2.rotateX(26.5 * Math.PI / 180);
    scene.add(ads2);

    let currentIndex = 0;
    let link = adsData[currentIndex].link;

    const loadingManager = new THREE.LoadingManager();

    loadingManager.onLoad = function () {
        // Las texturas han sido completamente cargadas
        materialAds.needsUpdate = true; // Actualiza el material una vez cargado
    };

    const textureLoader = new THREE.TextureLoader(loadingManager);

    function updateTexture() {
        // Cargar textura solo cuando sea necesario
        textureLoader.load(adsData[currentIndex].imagePath, (texture) => {
            texture.encoding = THREE.sRGBEncoding;
            texture.anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy());
            texture.minFilter = THREE.LinearFilter;
            texture.magFilter = THREE.LinearFilter;
            texture.generateMipmaps = true;

            // Solo actualizar la textura si ha cambiado
            materialAds.uniforms.uTexture.value = texture;
            materialAds.needsUpdate = true;
        });
        link = adsData[currentIndex].link;
        currentIndex = (currentIndex + 1) % adsData.length;
    }

    updateTexture();

    // Cambia la textura cada 5 segundos
    setInterval(updateTexture, 5000);

    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();

    function onMouseClick(event) {
        // if (document.getElementById('modal').style.display == 'block') return;
        const rect = renderer.domElement.getBoundingClientRect();
        mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
        mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);

        const intersects = raycaster.intersectObjects([ads, ads2]);
        if (intersects.length > 0) {
            const intersectedObject = intersects[0].object;
            const domain = link.match(/https?:\/\/(?:www\.)?([^\/]+)/);
            const result = domain ? domain[1] : null;
            if (confirm("Quieres ir a " + result)) {
                window.open(link, '_blank');
            }
        }
    }

    window.addEventListener('click', onMouseClick);
}

function createMesh(geometry, material, position) {
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.copy(position);
    return mesh;
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

function createBillboardCircle(radius, position, texturePath, saturation, link) {
    const geometry = new THREE.CircleGeometry(radius, 128);

    const textureLoader = new THREE.TextureLoader();
    const texture = textureLoader.load(texturePath, (texture) => {
        // Mejorar la calidad de la textura para dispositivos móviles
        texture.encoding = THREE.sRGBEncoding;
        texture.anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy()); // Mejorar la anisotropía en móviles
        texture.minFilter = THREE.LinearFilter; // Filtro lineal para mejorar la calidad
        texture.magFilter = THREE.LinearFilter;
        texture.generateMipmaps = true; // Asegurar que los mipmaps se generen correctamente
    });

    const material = new THREE.ShaderMaterial({
        uniforms: {
            uTexture: { value: texture },
            uSaturation: { value: saturation }
        },
        vertexShader: `
            varying vec2 vUv;
            void main() {
                vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `,
        fragmentShader: `
            uniform sampler2D uTexture;
            uniform float uSaturation;
            varying vec2 vUv;
            vec3 adjustSaturation(vec3 color, float saturation) {
                vec3 gray = vec3(dot(color, vec3(0.299, 0.587, 0.114)));
                return mix(gray, color, saturation);
            }
            void main() {
                vec4 texColor = texture2D(uTexture, vUv);
                texColor.rgb = adjustSaturation(texColor.rgb, uSaturation);
                gl_FragColor = texColor;
            }
        `,
        transparent: true,
        precision: 'highp'
    });

    const circle = new THREE.Mesh(geometry, material);
    circle.position.copy(position);
    scene.add(circle);

    const border = new THREE.Mesh(
        new THREE.RingGeometry(radius, radius + 10, 128),
        new THREE.MeshBasicMaterial({ color: 0xFAFA33 })
    );
    border.position.copy(position);
    scene.add(border);

    // Animar la rotación del billboard hacia la cámara
    animateBillboard(circle, border);

    // Agregar el manejador de clics con apertura del enlace en la misma pestaña
    addClickHandler(circle, link);
}

function addClickHandler(circle, link) {
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();

    function onMouseClick(event) {
        const rect = renderer.domElement.getBoundingClientRect();
        mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
        mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);
        const intersects = raycaster.intersectObject(circle);

        if (intersects.length > 0) {
            // Abrir el enlace en la misma pestaña
            window.location.href = link;
        }
    }

    window.addEventListener('click', onMouseClick);
}

function animateBillboard(circle, border) {
    function animate() {
        requestAnimationFrame(animate);
        circle.lookAt(camera.position);
        border.lookAt(camera.position);
        renderer.render(scene, camera);
    }
    animate();
}

function createTextGeometry(text, size, position, textColor, backgroundColor, lookAt, rotation) {
    const fontLoader = new FontLoader();

    fontLoader.load('./font/Roboto/Roboto_Bold.json', font => {
        // Crear la geometría del texto
        const textGeometry = new TextGeometry(text, {
            font,
            size,
            depth: 2,
            curveSegments: 12,
            bevelEnabled: true,
            bevelThickness: 1,
            bevelSize: 0.5,
            bevelOffset: 0,
            bevelSegments: 5,
        });

        textGeometry.center(); // Centra la geometría en el origen

        // Crear el material del texto
        const textMaterial = new THREE.MeshBasicMaterial({ color: textColor });
        const textMesh = new THREE.Mesh(textGeometry, textMaterial);

        // Calcular el tamaño del fondo
        const padding = size * 0.4;
        const bgWidth = textGeometry.boundingBox.max.x - textGeometry.boundingBox.min.x + padding * 2;
        const bgHeight = textGeometry.boundingBox.max.y - textGeometry.boundingBox.min.y + padding * 2;

        // Determinar si el fondo debe ser transparente
        const isTransparent = !backgroundColor; // Hacer el fondo transparente si no se proporciona color

        // Crear la geometría y el material del fondo
        const bgGeometry = new THREE.PlaneGeometry(bgWidth, bgHeight);
        const bgMaterial = new THREE.MeshBasicMaterial({
            color: isTransparent ? 0x000000 : backgroundColor, // Usar color negro si no se proporciona color
            transparent: isTransparent, // Hacer el material transparente si es necesario
        });
        const bgMesh = new THREE.Mesh(bgGeometry, bgMaterial);

        // Posicionar los meshes
        textMesh.position.set(position.x, position.y, position.z);
        bgMesh.position.set(position.x, position.y, position.z - 1);

        // Aplicar la rotación si se proporciona
        if (rotation) {
            textMesh.rotation.set(
                THREE.MathUtils.degToRad(rotation.x),
                THREE.MathUtils.degToRad(rotation.y),
                THREE.MathUtils.degToRad(rotation.z)
            );
            bgMesh.rotation.set(
                THREE.MathUtils.degToRad(rotation.x),
                THREE.MathUtils.degToRad(rotation.y),
                THREE.MathUtils.degToRad(rotation.z)
            );
        }

        // Añadir los meshes a la escena
        scene.add(bgMesh, textMesh);

        // Configurar el comportamiento de mirada si es necesario
        if (lookAt) {
            function updateMeshes() {
                textMesh.lookAt(camera.position);
                bgMesh.lookAt(camera.position);
            }

            function animateText() {
                requestAnimationFrame(animateText);
                updateMeshes();
                renderer.render(scene, camera);
            }

            animateText();
        } else {
            renderer.render(scene, camera);
        }
    });
}

function animateTextMesh(textMesh, bgMesh) {
    function animate() {
        requestAnimationFrame(animate);
        [textMesh, bgMesh].forEach(mesh => mesh.lookAt(camera.position));
        renderer.render(scene, camera);
    }
    animate();
}

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
}

init();

export { createBillboardCircle, createTextGeometry, createAds };