import * as THREE from 'three';
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';

export class DJ extends SceneFeature {
    constructor() {
        super();
        sceneFeatureManager.register(this);
    }

    init() {
        this.group = new THREE.Group();
        
        // Position behind the console (console is at z=-16.5)
        this.baseY = 1.8; // Stage height
        this.group.position.set(0, this.baseY, -17.5);
        
        // 1. Body (Blobby)
        const bodyGeo = new THREE.CapsuleGeometry(0.3, 0.8, 4, 8);
        const bodyMat = new THREE.MeshStandardMaterial({ 
            color: 0x3355ff, // Bright Blue
            roughness: 0.6, 
            metalness: 0.1 
        });
        const body = new THREE.Mesh(bodyGeo, bodyMat);
        body.position.y = 0.7;
        body.castShadow = true;
        body.receiveShadow = true;
        this.group.add(body);

        // 2. Head
        const headGeo = new THREE.SphereGeometry(0.25, 16, 16);
        const head = new THREE.Mesh(headGeo, bodyMat);
        head.position.y = 1.55;
        head.castShadow = true;
        head.receiveShadow = true;
        this.group.add(head);
        this.head = head;

        // 3. Glasses
        const glassesGroup = new THREE.Group();
        glassesGroup.position.set(0, 0, 0.18); // On face
        head.add(glassesGroup);

        const glassesMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.2, metalness: 0.8 });
        
        const lensGeo = new THREE.BoxGeometry(0.13, 0.08, 0.15);
        const bridgeGeo = new THREE.BoxGeometry(0.04, 0.02, 0.15);

        const leftLens = new THREE.Mesh(lensGeo, glassesMat);
        leftLens.position.set(-0.085, 0, 0);
        glassesGroup.add(leftLens);

        const rightLens = new THREE.Mesh(lensGeo, glassesMat);
        rightLens.position.set(0.085, 0, 0);
        glassesGroup.add(rightLens);

        const bridge = new THREE.Mesh(bridgeGeo, glassesMat);
        glassesGroup.add(bridge);

        // 4. Headphones
        const headphoneMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.3, metalness: 0.8 });
        const earCupGeo = new THREE.CylinderGeometry(0.12, 0.12, 0.1, 16);
        earCupGeo.rotateZ(Math.PI / 2);
        
        const leftCup = new THREE.Mesh(earCupGeo, headphoneMat);
        leftCup.position.set(-0.26, 0, 0);
        head.add(leftCup);
        
        const rightCup = new THREE.Mesh(earCupGeo, headphoneMat);
        rightCup.position.set(0.26, 0, 0);
        head.add(rightCup);
        
        const bandGeo = new THREE.TorusGeometry(0.26, 0.03, 8, 24, Math.PI);
        const band = new THREE.Mesh(bandGeo, headphoneMat);
        band.position.set(0, 0, 0);
        head.add(band);

        // 5. Arms
        const armGeo = new THREE.SphereGeometry(0.12, 16, 16);
        const createArm = (isLeft) => {
            const pivot = new THREE.Group();
            pivot.position.set(isLeft ? -0.35 : 0.35, 1.3, 0);
            
            const arm = new THREE.Mesh(armGeo, bodyMat);
            arm.scale.set(1, 3.5, 1);
            arm.position.y = -0.4;
            arm.castShadow = true;
            arm.receiveShadow = true;
            
            pivot.add(arm);
            return pivot;
        };

        this.leftArm = createArm(true);
        this.rightArm = createArm(false);
        this.group.add(this.leftArm);
        this.group.add(this.rightArm);

        this.group.visible = false;
        state.scene.add(this.group);

        // Movement State
        this.state = 'IDLE';
        this.targetX = 0;
        this.moveTimer = 0;

        // Arm State
        this.armState = 3; // 0: None, 1: Left, 2: Right, 3: Both, 4: Twiddling
        this.armTimer = 0;
        this.currentLeftAngle = Math.PI * 0.85;
        this.currentRightAngle = Math.PI * 0.85;
        this.currentLeftAngleX = 0;
        this.currentRightAngleX = 0;

        // --- Hats ---
        this.hats = {};

        // Santa Hat
        const santaGroup = new THREE.Group();
        const santaCone = new THREE.Mesh(new THREE.ConeGeometry(0.14, 0.3, 16), new THREE.MeshStandardMaterial({ color: 0xff0000 }));
        santaCone.position.y = 0.15;
        santaCone.rotation.x = -0.2;
        const santaRim = new THREE.Mesh(new THREE.TorusGeometry(0.14, 0.04, 8, 16), new THREE.MeshStandardMaterial({ color: 0xffffff }));
        santaRim.rotation.x = Math.PI/2;
        const santaBall = new THREE.Mesh(new THREE.SphereGeometry(0.04), new THREE.MeshStandardMaterial({ color: 0xffffff }));
        santaBall.position.set(0, 0.28, -0.08);
        santaGroup.add(santaCone, santaRim, santaBall);
        santaGroup.position.set(0, 0.22, 0);
        santaGroup.visible = false;
        this.head.add(santaGroup);
        this.hats['Santa'] = santaGroup;

        // Top Hat
        const topHatGroup = new THREE.Group();
        const brim = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 0.02, 16), new THREE.MeshStandardMaterial({ color: 0x111111 }));
        const cylinder = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.15, 0.25, 16), new THREE.MeshStandardMaterial({ color: 0x111111 }));
        cylinder.position.y = 0.125;
        topHatGroup.add(brim, cylinder);
        topHatGroup.position.set(0, 0.22, 0);
        topHatGroup.visible = false;
        this.head.add(topHatGroup);
        this.hats['Top Hat'] = topHatGroup;

        // --- Gameboy / Portable Console ---
        const gbGroup = new THREE.Group();
        // Body
        const gbGeo = new THREE.BoxGeometry(0.2, 0.3, 0.05);
        const gbMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.4 });
        const gbBody = new THREE.Mesh(gbGeo, gbMat);
        gbGroup.add(gbBody);

        // Screen (Emissive)
        const screenGeo = new THREE.PlaneGeometry(0.16, 0.12);
        this.gbScreenMat = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); 
        const screen = new THREE.Mesh(screenGeo, this.gbScreenMat);
        screen.position.set(0, 0.05, 0.026);
        gbGroup.add(screen);

        // Knobs/Buttons
        const knobGeo = new THREE.CylinderGeometry(0.02, 0.02, 0.02, 8);
        const knobMat = new THREE.MeshStandardMaterial({ color: 0xcccccc });
        
        const k1 = new THREE.Mesh(knobGeo, knobMat);
        k1.rotation.x = Math.PI/2;
        k1.position.set(-0.05, -0.08, 0.026);
        gbGroup.add(k1);

        const k2 = new THREE.Mesh(knobGeo, knobMat);
        k2.rotation.x = Math.PI/2;
        k2.position.set(0.05, -0.08, 0.026);
        gbGroup.add(k2);

        // Attach to Left Arm (Hand position)
        gbGroup.position.set(0, -0.9, 0.1); 
        gbGroup.rotation.x = -Math.PI / 4; // Tilted up
        gbGroup.rotation.z = Math.PI; // Upside down
        this.leftArm.add(gbGroup);
        this.gameboy = gbGroup;
    }

    update(deltaTime) {
        if (!this.group || !state.partyStarted) return;

        const time = state.clock.getElapsedTime();
        
        // Bobbing
        let bobAmount = 0;
        let beatIntensity = 0;
        if (state.music) {
            beatIntensity = state.music.beatIntensity;
            bobAmount = Math.sin(time * 15) * 0.05 * (0.5 + beatIntensity);
        }
        this.group.position.y = this.baseY + bobAmount;

        // Arms Up Animation
        // Update Arm State
        this.armTimer -= deltaTime;
        if (this.armTimer <= 0) {
            this.armState = Math.floor(Math.random() * 4); // 0-3: Dance moves
            this.armTimer = 2 + Math.random() * 4;
            
            const rand = Math.random();
            // Gameboy Fiddling
            if (state.config.gameboyEnabled && rand < 0.35) {
                this.armState = 5;
            } else if (state.config.consoleEnabled && rand < 0.65) {
                this.armState = 4; // Console Twiddling
            }
        }

        const upAngle = Math.PI * 0.85;
        const downAngle = 0.1;
        const twiddleX = -1.2;

        let targetLeftZ = downAngle;
        let targetRightZ = downAngle;
        let targetLeftX = 0;
        let targetRightX = 0;

        if (this.armState === 4 && state.config.consoleEnabled) {
            // Twiddling
            targetLeftZ = 0.2;
            targetRightZ = 0.2;
            targetLeftX = twiddleX;
            targetRightX = twiddleX;
        } else if (this.armState === 5 && state.config.gameboyEnabled) {
            targetLeftZ = -0.3;
            targetRightZ = -0.5; // Cross inwards
            const timeRotX = Math.cos(time * 0.5) * 0.3;
            const consoleRotX = state.config.consoleEnabled ? -0.5 : 0;
            targetLeftX = -1.0 + timeRotX + consoleRotX; // Lift up
            targetRightX = -1.1 + timeRotX + consoleRotX; // Lift up
        } else {
            targetLeftZ = (this.armState === 1 || this.armState === 3) ? upAngle : downAngle;
            targetRightZ = (this.armState === 2 || this.armState === 3) ? upAngle : downAngle;
        }

        this.currentLeftAngle = THREE.MathUtils.lerp(this.currentLeftAngle, targetLeftZ, deltaTime * 3);
        this.currentRightAngle = THREE.MathUtils.lerp(this.currentRightAngle, targetRightZ, deltaTime * 3);
        this.currentLeftAngleX = THREE.MathUtils.lerp(this.currentLeftAngleX, targetLeftX, deltaTime * 5);
        this.currentRightAngleX = THREE.MathUtils.lerp(this.currentRightAngleX, targetRightX, deltaTime * 5);

        if (this.armState === 4 && state.config.consoleEnabled) {
            const t = time * 15;
            this.leftArm.rotation.z = -this.currentLeftAngle + Math.cos(t) * 0.05;
            this.rightArm.rotation.z = this.currentRightAngle + Math.sin(t) * 0.05;
            this.leftArm.rotation.x = this.currentLeftAngleX + Math.sin(t) * 0.1;
            this.rightArm.rotation.x = this.currentRightAngleX + Math.cos(t) * 0.1;
        } else if (this.armState === 5 && state.config.gameboyEnabled) {
            const t = time * 20;
            this.leftArm.rotation.z = -this.currentLeftAngle + Math.sin(t * 0.2) * 0.01;
            this.rightArm.rotation.z = this.currentRightAngle + Math.sin(t) * 0.05;
            this.leftArm.rotation.x = this.currentLeftAngleX + Math.cos(t * 0.2) * 0.01;
            this.rightArm.rotation.x = this.currentRightAngleX + Math.cos(t) * 0.05;
        } else {
            const wave = Math.sin(time * 8) * 0.1;
            const beatBounce = beatIntensity * 0.2;
            this.leftArm.rotation.z = -this.currentLeftAngle + wave - beatBounce;
            this.rightArm.rotation.z = this.currentRightAngle - wave + beatBounce;
            this.leftArm.rotation.x = this.currentLeftAngleX;
            this.rightArm.rotation.x = this.currentRightAngleX;
        }
        
        // Head Bop
        this.head.rotation.x = beatIntensity * 0.2;

        // Movement Logic: Slide along the console
        if (this.state === 'IDLE') {
            this.moveTimer -= deltaTime;
            if (this.moveTimer <= 0) {
                this.state = 'MOVING';
                const range = state.config.consoleEnabled ? 2.5 : 10.0;
                this.targetX = (Math.random() - 0.5) * range; 
            }
        } else if (this.state === 'MOVING') {
            const speed = 1.5;
            if (Math.abs(this.group.position.x - this.targetX) < 0.1) {
                this.state = 'IDLE';
                this.moveTimer = 2 + Math.random() * 5; // Wait 2-7 seconds before moving again
            } else {
                const dir = Math.sign(this.targetX - this.group.position.x);
                this.group.position.x += dir * speed * deltaTime;
            }
        }

        // Update Hat Visibility
        for (const [name, mesh] of Object.entries(this.hats)) {
            mesh.visible = (state.config.djHat === name);
        }

        // Update Gameboy
        if (this.gameboy) {
            this.gameboy.visible = state.config.gameboyEnabled;
            if (this.gameboy.visible && state.music) {
                const hue = (time * 0.5) % 1;
                this.gbScreenMat.color.setHSL(hue, 1.0, 0.5);
            }
        }
    }
    
    onPartyStart() {
        if(this.group) this.group.visible = true;
    }
    
    onPartyEnd() {
        // Optional: Hide DJ or stop movement
    }
}

new DJ();