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

export class MusicVisualizer extends SceneFeature {
    constructor() {
        super();
        this.lastStateChangeTime = 0;
        this.lastBlackoutMode = false;
        this.lastBeatTime = 0;
        this.beatIntervals = [];
        this.beatThreshold = 0.3;
        this.loudnessHistory = [];
        this.loudnessLowsHistory = [];
        sceneFeatureManager.register(this);
    }

    init() {
        // Initialize music state
        state.music = {
            beatDuration: 60 / 120,
            measureDuration: (60 / 120) * 4,
            beatIntensity: 0,
            measurePulse: 0,
            isLoudEnough: false,
            thresholds: { loud: 0, quiet: 0 },
            mode: 'Normal',
            bpm: 1,
            loudness: 0,
            loudnessLows: 0,
            loudnessHighs: 0,
            loudnessAverage: 0,
            loudnessLowsAverage: 0,
            frequencyData: null,
            beatThreshold: 0
        };
        this.beatPhase = 0;
        this.measurePhase = 0;
        this.averageLoudness = 0;
        this.prevLoudness = 0;
        this.prevLoudnessLows = 0;
        this.prevLoudnessHighs = 0;
        this.beatSignalAvg = 0;

        // 3 Detectors: Lows (Bass), Highs (Snare/Hats), Flux (All)
        this.detectors = [
            { name: 'low', lastTime: 0, threshold: 0.3, intervals: [], bpm: 120, weight: 1.0 },
            { name: 'high', lastTime: 0, threshold: 0.3, intervals: [], bpm: 120, weight: 0.5 },
            { name: 'flux', lastTime: 0, threshold: 0.3, intervals: [], bpm: 120, weight: 0.8 }
        ];
    }

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

        // --- Calculate Loudness from Frequency Data ---
        if (state.music.frequencyData) {
            const dataArray = state.music.frequencyData;
            let sumLows = 0;
            let sumHighs = 0;
            let sumAll = 0;
            const countLows = Math.floor(Math.max(2, dataArray.length * 0.2)); // Focus on bottom 10% for bass
            const countHighs = Math.min(dataArray.length * 0.6, dataArray.length);
            
            for (let i = 0; i < dataArray.length; i++) {
                sumAll += dataArray[i];
                if (i < countLows) {
                    sumLows += dataArray[i];
                }
                if (i > dataArray.length - countHighs) {
                    sumHighs += dataArray[i];
                }
            }
            
            const averageLows = sumLows / countLows;
            const averageHighs = sumHighs / countHighs;
            const averageAll = sumAll / dataArray.length;
            
            // Normalize to 0-1 range
            state.music.loudness = averageAll / 255;
            state.music.loudnessLows = averageLows / 255;
            state.music.loudnessHighs = averageHighs / 255;

            // --- Track loudness over the last X seconds ---
            const loudnessHistorySeconds = 0.5;
            this.loudnessHistory.push(state.music.loudness);
            if (this.loudnessHistory.length > loudnessHistorySeconds*60) this.loudnessHistory.shift();
            
            this.loudnessLowsHistory.push(state.music.loudnessLows);
            if (this.loudnessLowsHistory.length > loudnessHistorySeconds*60) this.loudnessLowsHistory.shift();

            const avgLoudness = this.loudnessHistory.reduce((a, b) => a + b, 0) / this.loudnessHistory.length;
            const avgLowsLoudness = this.loudnessLowsHistory.reduce((a, b) => a + b, 0) / this.loudnessLowsHistory.length;
            
            state.music.loudnessAverage = avgLoudness;
            state.music.loudnessLowsAverage = avgLowsLoudness;
            state.music.isLoudEnough = avgLoudness > 0.1;
        }

        const time = state.clock.getElapsedTime();
        const loudness = state.music.loudnessLows || 0;
        const loudnessAvg = state.music.loudnessLowsAverage || 0;
        const songTime = state.music.player ? state.music.player.currentTime : 0;
        this.averageLoudness = THREE.MathUtils.lerp(this.averageLoudness, loudnessAvg, deltaTime * (songTime < 5 ? 0.5 : 0.05));
        state.music.averageLoudness = this.averageLoudness;

        // --- Beat Detection & Auto-BPM ---
        // 1. Calculate derivatives for 3 bands
        const dLows = Math.max(0, state.music.loudnessLows - this.prevLoudnessLows);
        const dHighs = Math.max(0, state.music.loudnessHighs - this.prevLoudnessHighs);
        const dAll = Math.max(0, state.music.loudness - this.prevLoudness);
        
        this.prevLoudnessLows = state.music.loudnessLows;
        this.prevLoudnessHighs = state.music.loudnessHighs;
        this.prevLoudness = state.music.loudness;

        const signals = [dLows * 10.0, dHighs * 10.0, dAll * 10.0];
        const now = time;
        let mainTrigger = false;

        // 2. Update Detectors
        this.detectors.forEach((d, i) => {
            const signal = signals[i];
            
            // Decay threshold
            d.threshold -= deltaTime * 1.5;
            const floor = 0.15;
            if (d.threshold < floor) d.threshold = floor;

            if (signal > d.threshold) {
                const interval = now - d.lastTime;
                // Debounce (Max 240 BPM)
                if (interval > 0.25) {
                    d.lastTime = now;
                    d.threshold = signal * 1.2;

                    // Valid interval (30 - 300 BPM)
                    if (interval < 2.0) {
                        d.intervals.push(interval);
                        if (d.intervals.length > 8) d.intervals.shift();
                        
                        const avgInterval = d.intervals.reduce((a, b) => a + b, 0) / d.intervals.length;
                        d.bpm = 60 / avgInterval;
                    }
                    
                    if (i === 0) mainTrigger = true; // Bass triggers visual beat
                }
            }
        });

        // 3. Combine Results
        let bpmSum = 0;
        let weightSum = 0;
        this.detectors.forEach(d => {
            let bpm = d.bpm;
            // Normalize to 90-180 range for consensus
            if (bpm > 0) {
                while (bpm < 90) bpm *= 2;
                while (bpm > 180) bpm /= 2;
                bpmSum += bpm * d.weight;
                weightSum += d.weight;
            }
        });
        
        if (weightSum > 0) {
            const targetBPM = bpmSum / weightSum;
            const targetDuration = 60 / targetBPM;
            state.music.beatDuration = THREE.MathUtils.lerp(state.music.beatDuration, targetDuration, deltaTime * 0.5);
            state.music.measureDuration = state.music.beatDuration * 4;
            state.music.bpm = Math.round(60 / state.music.beatDuration);
        }

        // Visuals use the Bass detector for immediate feedback
        state.music.beatThreshold = this.detectors[0].threshold;
        const lastBeatInterval = now - this.detectors[0].lastTime;
        
        if (mainTrigger) {
            this.beatPhase = 0;
        }

        // --- Phase Accumulation ---
        this.beatPhase += deltaTime / state.music.beatDuration;
        if (this.beatPhase >= 1) this.beatPhase -= 1;

        this.measurePhase += deltaTime / state.music.measureDuration;
        if (this.measurePhase >= 1) this.measurePhase -= 1;

        // --- Calculate Beat Intensity ---
        const realBeatFactor = lastBeatInterval < 1.0 ? 1.0 : ((lastBeatInterval > 5) ? 0.0 : (5 - lastBeatInterval)/5);
        state.music.beatIntensity = Math.pow(1.0 - this.beatPhase, 2) * realBeatFactor;

        // --- Calculate Measure Pulse ---
        state.music.measurePulse = this.measurePhase < 0.2 ? Math.sin(this.measurePhase * Math.PI * 5) : 0;

        if (state.blackoutMode !== this.lastBlackoutMode) {
            this.lastStateChangeTime = time;
            this.lastBlackoutMode = state.blackoutMode;
        }

        const timeInState = time - this.lastStateChangeTime;
        let timeInStateLoud = 0;
        let timeInStateQuiet = 0;

        if (state.blackoutMode) {
            timeInStateQuiet = timeInState;
        } else {
            timeInStateLoud = timeInState;
        }

        // Dynamic Thresholds
        // Enter blackout if loudness falls below 80% of average
        let quietThreshold = this.averageLoudness * 0.8;
        
        // Exit blackout if loudness returns to normal (average) or above
        let loudThreshold = this.averageLoudness;

        // --- Auto-Blackout Logic ---
        // If blackout is active, monitor for loud events (The Drop) to disable it.
        if (state.config.blackout) {
            if (state.blackoutMode) {
                if (loudness >= loudThreshold) {
                    state.blackoutMode = false;
                }
            } else {
                if (loudness < quietThreshold) {
                    state.blackoutMode = true;
                }
            }
        } else {
            state.blackoutMode = false;
        }

        state.music.thresholds = { loud: loudThreshold, quiet: quietThreshold };

        if (loudness > loudThreshold) {
            state.music.mode = 'Loud';
        } else if (loudness < quietThreshold) {
            state.music.mode = 'Quiet';
        } else {
            state.music.mode = 'Normal';
        }
    }
}

new MusicVisualizer();