import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import TEAM_LOCATIONS from './TeamGlobeData.js';
import { formatInTimeZone } from 'date-fns-tz';

const GLOBE_RADIUS = 5;
const MARKER_HEIGHT = 0.4;
const MARKER_RADIUS = 0.08;

function latLngToVector3(lat, lng, radius) {
    const phi = (90 - lat) * (Math.PI / 180);
    const theta = (lng + 180) * (Math.PI / 180);
    return new THREE.Vector3(
        -(radius * Math.sin(phi) * Math.cos(theta)),
        radius * Math.cos(phi),
        radius * Math.sin(phi) * Math.sin(theta)
    );
}

function createAtmosphere() {
    const geometry = new THREE.SphereGeometry(GLOBE_RADIUS * 1.05, 64, 64);
    const material = new THREE.ShaderMaterial({
        vertexShader: `
            varying vec3 vNormal;
            void main() {
                vNormal = normalize(normalMatrix * normal);
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `,
        fragmentShader: `
            varying vec3 vNormal;
            void main() {
                float intensity = pow(0.65 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.0);
                gl_FragColor = vec4(0.3, 0.6, 1.0, 1.0) * intensity;
            }
        `,
        blending: THREE.AdditiveBlending,
        side: THREE.BackSide,
        transparent: true,
    });
    return new THREE.Mesh(geometry, material);
}

function createMarker(location) {
    const group = new THREE.Group();

    const surfacePos = latLngToVector3(location.lat, location.lng, GLOBE_RADIUS);
    const outerPos = latLngToVector3(location.lat, location.lng, GLOBE_RADIUS + MARKER_HEIGHT);
    const normal = surfacePos.clone().normalize();

    // Pin line
    const lineGeometry = new THREE.BufferGeometry().setFromPoints([surfacePos, outerPos]);
    const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.6 });
    group.add(new THREE.Line(lineGeometry, lineMaterial));

    // Pin head sphere
    const sphereGeometry = new THREE.SphereGeometry(MARKER_RADIUS, 16, 16);
    const sphereMaterial = new THREE.MeshPhongMaterial({
        color: location.color,
        emissive: location.color,
        emissiveIntensity: 0.5,
    });
    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.copy(outerPos);
    group.add(sphere);

    // Glow ring at base
    const ringGeometry = new THREE.RingGeometry(0.06, 0.12, 32);
    const ringMaterial = new THREE.MeshBasicMaterial({
        color: location.color,
        transparent: true,
        opacity: 0.4,
        side: THREE.DoubleSide,
    });
    const ring = new THREE.Mesh(ringGeometry, ringMaterial);
    ring.position.copy(surfacePos.clone().add(normal.clone().multiplyScalar(0.01)));
    ring.lookAt(ring.position.clone().add(normal));
    group.add(ring);

    return { group, labelPosition: outerPos, normal };
}

function createLabel(location, index) {
    const div = document.createElement('div');
    div.style.cssText = `
        color: #fff;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
        font-size: 11px;
        background: rgba(0, 0, 0, 0.75);
        border: 1px solid rgba(255, 255, 255, 0.2);
        border-radius: 4px;
        padding: 4px 8px;
        pointer-events: none;
        white-space: nowrap;
        line-height: 1.4;
    `;
    div.innerHTML = `
        <div style="font-weight: 600; color: #${location.color.toString(16).padStart(6, '0')}">${location.name}</div>
        <div style="opacity: 0.8">${location.staff} staff</div>
        <div class="globe-label-time" data-location-index="${index}" style="font-variant-numeric: tabular-nums; opacity: 0.7"></div>
    `;
    const label = new CSS2DObject(div);
    label.position.copy(latLngToVector3(location.lat, location.lng, GLOBE_RADIUS + MARKER_HEIGHT + 0.15));
    return label;
}

export default function TeamGlobeViewer() {
    const containerRef = useRef(null);

    useEffect(() => {
        if (!containerRef.current) return;

        const container = containerRef.current;
        const width = container.offsetWidth;
        const height = Math.min(width, 700);

        // Scene
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x0a0e17);

        // Camera
        const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
        camera.position.z = 16;

        // WebGL renderer
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(width, height);
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        container.appendChild(renderer.domElement);

        // CSS2D renderer for labels
        const labelRenderer = new CSS2DRenderer();
        labelRenderer.setSize(width, height);
        labelRenderer.domElement.style.position = 'absolute';
        labelRenderer.domElement.style.top = '0';
        labelRenderer.domElement.style.left = '0';
        labelRenderer.domElement.style.pointerEvents = 'none';
        container.appendChild(labelRenderer.domElement);

        // Lighting
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
        scene.add(ambientLight);
        const dirLight = new THREE.DirectionalLight(0xffffff, 1.0);
        dirLight.position.set(5, 3, 5);
        scene.add(dirLight);

        // Globe
        const globeGroup = new THREE.Group();
        scene.add(globeGroup);

        const textureLoader = new THREE.TextureLoader();
        const earthTexture = textureLoader.load('/images/earth_texture.jpg');
        earthTexture.colorSpace = THREE.SRGBColorSpace;

        const globeGeometry = new THREE.SphereGeometry(GLOBE_RADIUS, 64, 64);
        const globeMaterial = new THREE.MeshPhongMaterial({
            map: earthTexture,
            bumpScale: 0.05,
        });
        const globe = new THREE.Mesh(globeGeometry, globeMaterial);
        globeGroup.add(globe);

        // Atmosphere
        globeGroup.add(createAtmosphere());

        // Markers and labels
        const labels = [];
        const markerNormals = [];
        TEAM_LOCATIONS.forEach((location, index) => {
            const { group, normal } = createMarker(location);
            globeGroup.add(group);

            const label = createLabel(location, index);
            globeGroup.add(label);
            labels.push(label);
            markerNormals.push(normal);
        });

        // Mouse interaction state
        let isDragging = false;
        let previousMouse = { x: 0, y: 0 };
        let rotationVelocity = { x: 0, y: 0 };
        let autoRotateSpeed = 0.002;
        let idleTimer = 0;

        const onMouseDown = (e) => {
            isDragging = true;
            previousMouse = { x: e.clientX, y: e.clientY };
            idleTimer = 0;
        };
        const onMouseMove = (e) => {
            if (!isDragging) return;
            const deltaX = e.clientX - previousMouse.x;
            const deltaY = e.clientY - previousMouse.y;
            rotationVelocity.x = deltaY * 0.005;
            rotationVelocity.y = deltaX * 0.005;
            globeGroup.rotation.x += rotationVelocity.x;
            globeGroup.rotation.y += rotationVelocity.y;
            previousMouse = { x: e.clientX, y: e.clientY };
            idleTimer = 0;
        };
        const onMouseUp = () => {
            isDragging = false;
        };
        const onWheel = (e) => {
            e.preventDefault();
            camera.position.z = Math.max(9, Math.min(25, camera.position.z + e.deltaY * 0.01));
            idleTimer = 0;
        };

        // Touch support
        const onTouchStart = (e) => {
            if (e.touches.length === 1) {
                isDragging = true;
                previousMouse = { x: e.touches[0].clientX, y: e.touches[0].clientY };
                idleTimer = 0;
            }
        };
        const onTouchMove = (e) => {
            if (!isDragging || e.touches.length !== 1) return;
            const deltaX = e.touches[0].clientX - previousMouse.x;
            const deltaY = e.touches[0].clientY - previousMouse.y;
            rotationVelocity.x = deltaY * 0.005;
            rotationVelocity.y = deltaX * 0.005;
            globeGroup.rotation.x += rotationVelocity.x;
            globeGroup.rotation.y += rotationVelocity.y;
            previousMouse = { x: e.touches[0].clientX, y: e.touches[0].clientY };
            idleTimer = 0;
        };
        const onTouchEnd = () => {
            isDragging = false;
        };

        const canvas = renderer.domElement;
        canvas.addEventListener('mousedown', onMouseDown);
        canvas.addEventListener('mousemove', onMouseMove);
        canvas.addEventListener('mouseup', onMouseUp);
        canvas.addEventListener('mouseleave', onMouseUp);
        canvas.addEventListener('wheel', onWheel, { passive: false });
        canvas.addEventListener('touchstart', onTouchStart, { passive: true });
        canvas.addEventListener('touchmove', onTouchMove, { passive: true });
        canvas.addEventListener('touchend', onTouchEnd);

        // Time update interval
        const timeInterval = setInterval(() => {
            TEAM_LOCATIONS.forEach((loc, i) => {
                const el = container.querySelector(`.globe-label-time[data-location-index="${i}"]`);
                if (el) {
                    el.textContent = formatInTimeZone(new Date(), loc.timezone, 'h:mm:ss a zzz');
                }
            });
        }, 1000);

        // Animation loop
        let animFrameId;
        const animate = () => {
            animFrameId = requestAnimationFrame(animate);

            // Auto-rotate when idle
            idleTimer++;
            if (!isDragging && idleTimer > 120) {
                globeGroup.rotation.y += autoRotateSpeed;
            }

            // Damping
            if (!isDragging) {
                rotationVelocity.x *= 0.95;
                rotationVelocity.y *= 0.95;
                globeGroup.rotation.x += rotationVelocity.x;
                globeGroup.rotation.y += rotationVelocity.y;
            }

            // Label culling: hide labels on far side of globe
            const cameraDir = new THREE.Vector3();
            camera.getWorldDirection(cameraDir);
            labels.forEach((label, i) => {
                const markerWorldPos = new THREE.Vector3();
                label.getWorldPosition(markerWorldPos);
                const toMarker = markerWorldPos.clone().sub(camera.position).normalize();
                const markerNormalWorld = markerNormals[i].clone().applyQuaternion(globeGroup.quaternion).normalize();
                const dot = markerNormalWorld.dot(toMarker);
                label.visible = dot < -0.1;
            });

            renderer.render(scene, camera);
            labelRenderer.render(scene, camera);
        };
        animate();

        // Trigger initial time update
        TEAM_LOCATIONS.forEach((loc, i) => {
            const el = container.querySelector(`.globe-label-time[data-location-index="${i}"]`);
            if (el) {
                el.textContent = formatInTimeZone(new Date(), loc.timezone, 'h:mm:ss a zzz');
            }
        });

        // Resize handler
        const onResize = () => {
            const w = container.offsetWidth;
            const h = Math.min(w, 700);
            camera.aspect = w / h;
            camera.updateProjectionMatrix();
            renderer.setSize(w, h);
            labelRenderer.setSize(w, h);
        };
        window.addEventListener('resize', onResize);

        // Cleanup
        return () => {
            cancelAnimationFrame(animFrameId);
            clearInterval(timeInterval);
            window.removeEventListener('resize', onResize);
            canvas.removeEventListener('mousedown', onMouseDown);
            canvas.removeEventListener('mousemove', onMouseMove);
            canvas.removeEventListener('mouseup', onMouseUp);
            canvas.removeEventListener('mouseleave', onMouseUp);
            canvas.removeEventListener('wheel', onWheel);
            canvas.removeEventListener('touchstart', onTouchStart);
            canvas.removeEventListener('touchmove', onTouchMove);
            canvas.removeEventListener('touchend', onTouchEnd);
            renderer.dispose();
            if (container.contains(renderer.domElement)) container.removeChild(renderer.domElement);
            if (container.contains(labelRenderer.domElement)) container.removeChild(labelRenderer.domElement);
        };
    }, []);

    return (
        <div
            ref={containerRef}
            style={{
                position: 'relative',
                width: '100%',
                borderRadius: '8px',
                overflow: 'hidden',
                cursor: 'grab',
            }}
        />
    );
}
