// web/src/audio.ts - Звуковой движок для сигнала

export class AudioEngine {
    private audioContext: AudioContext | null = null;
    private oscillator: OscillatorNode | null = null;
    private gainNode: GainNode | null = null;
    private isPlaying: boolean = false;
    private currentLevel: number = 0;
    private currentFrequency: number = 800;
    private fadeTimeout: number | null = null;
    private readonly FADE_TIME: number = 0.05;
    private readonly FREQUENCY_MAP: Record<number, number> = {
        1: 600,   // Уровень 1 → 600 Гц
        2: 1000,  // Уровень 2 → 1000 Гц
        3: 1200   // Уровень 3 → 1200 Гц
    };

    // ===== НОВОЕ: громкость (0-100) =====
    private volume: number = 0;
    private maxVolume: number = 0.45; // 45% максимум

    constructor() {
        // Инициализация при первом использовании
    }

    /**
     * Установка громкости (0-100)
     */
    public setVolume(volume: number): void {
        this.volume = Math.max(0, Math.min(100, volume));
        this.updateVolume();
    }

    /**
     * Получение текущей громкости (0-100)
     */
    public getVolume(): number {
        return this.volume;
    }

    /**
     * Применение громкости к усилителю
     */
    private updateVolume(): void {
        if (!this.gainNode) return;

        const normalized = (this.volume / 100) * this.maxVolume;
        const currentLevel = this.currentLevel;

        // Если уровень 0 или громкость 0 - выключаем звук
        if (currentLevel === 0 || this.volume === 0) {
            this.gainNode.gain.value = 0;
            return;
        }

        // Рассчитываем громкость на основе уровня и общей громкости
        const volumeMap: Record<number, number> = {
            1: 0.10 * (normalized / this.maxVolume),
            2: 0.25 * (normalized / this.maxVolume),
            3: 0.45 * (normalized / this.maxVolume)
        };

        const targetVolume = Math.min(volumeMap[currentLevel] || 0, this.maxVolume * (this.volume / 100));

        try {
            const currentTime = this.audioContext?.currentTime || 0;
            this.gainNode.gain.exponentialRampToValueAtTime(
                Math.max(targetVolume, 0.001),
                currentTime + this.FADE_TIME
            );
        } catch (error) {
            this.gainNode.gain.value = targetVolume;
        }
    }

    // ===== ОСТАЛЬНЫЕ МЕТОДЫ =====

    private async initAudio(): Promise<boolean> {
        try {
            if (!this.audioContext) {
                const AudioContextClass = (window as any).AudioContext || (window as any).webkitAudioContext;
                this.audioContext = new AudioContextClass();
            }

            const context = this.audioContext;
            if (!context) return false;

            if (context.state === 'suspended') {
                await context.resume();
            }

            return context.state === 'running';
        } catch (error) {
            console.warn('[Audio] Ошибка инициализации:', error);
            return false;
        }
    }

    /**
     * Получение частоты для уровня
     */
    private getFrequencyForLevel(level: number): number {
        return this.FREQUENCY_MAP[level] || 800;
    }

    /**
     * Обновление частоты осциллятора
     */
    private updateFrequency(frequency: number): void {
        if (!this.oscillator) return;

        try {
            const currentTime = this.audioContext?.currentTime || 0;
            this.oscillator.frequency.exponentialRampToValueAtTime(
                frequency,
                currentTime + this.FADE_TIME
            );
            this.currentFrequency = frequency;
        } catch (error) {
            this.oscillator.frequency.value = frequency;
            this.currentFrequency = frequency;
        }
    }

    /**
     * Создание звукового графа
     */
    private createSoundGraph(level: number): void {
        if (!this.audioContext) return;

        this.stopSound();

        const frequency = this.getFrequencyForLevel(level);
        this.currentFrequency = frequency;

        this.oscillator = this.audioContext.createOscillator();
        this.oscillator.type = 'sine';
        this.oscillator.frequency.value = frequency;

        this.gainNode = this.audioContext.createGain();
        this.gainNode.gain.value = 0;

        this.oscillator.connect(this.gainNode);
        this.gainNode.connect(this.audioContext.destination);

        this.oscillator.start();
        this.isPlaying = true;

        // Применяем текущую громкость
        this.updateVolume();
    }

    /**
     * Обновление уровня сигнала
     * @param level - уровень сигнала (0-3)
     */
    public async updateLevel(level: number): Promise<void> {
        if (!this.audioContext) {
            const initialized = await this.initAudio();
            if (!initialized) return;
        }

        const context = this.audioContext;
        if (!context) return;

        if (context.state !== 'running') {
            try {
                await context.resume();
            } catch (error) {
                console.warn('[Audio] Ошибка возобновления контекста:', error);
                return;
            }
        }

        const newLevel = Math.max(0, Math.min(3, Math.round(level)));

        if (newLevel === this.currentLevel) {
            return;
        }

        this.currentLevel = newLevel;

        // Если громкость 0 - ничего не включаем
        if (this.volume === 0) {
            this.stopSound();
            return;
        }

        const newFrequency = this.getFrequencyForLevel(newLevel);

        if (newLevel > 0 && !this.oscillator) {
            this.createSoundGraph(newLevel);
            return;
        }

        if (newLevel === 0) {
            this.fadeOut();
            return;
        }

        if (!this.isPlaying || !this.oscillator) {
            this.createSoundGraph(newLevel);
            return;
        }

        if (newFrequency !== this.currentFrequency) {
            this.updateFrequency(newFrequency);
        }

        this.updateVolume();
    }

    /**
     * Плавное затухание и остановка
     */
    private fadeOut(): void {
        if (!this.gainNode || !this.audioContext) return;

        if (this.fadeTimeout) {
            clearTimeout(this.fadeTimeout);
            this.fadeTimeout = null;
        }

        try {
            const currentTime = this.audioContext.currentTime;
            this.gainNode.gain.exponentialRampToValueAtTime(0.001, currentTime + this.FADE_TIME * 2);
        } catch (error) {
            this.gainNode.gain.value = 0;
        }

        this.fadeTimeout = window.setTimeout(() => {
            this.stopSound();
            this.fadeTimeout = null;
        }, this.FADE_TIME * 2 * 1000 + 50);
    }

    /**
     * Немедленная остановка звука
     */
    private stopSound(): void {
        if (this.oscillator) {
            try {
                this.oscillator.stop();
                this.oscillator.disconnect();
            } catch (e) {}
            this.oscillator = null;
        }

        if (this.gainNode) {
            try {
                this.gainNode.disconnect();
            } catch (e) {}
            this.gainNode = null;
        }

        this.isPlaying = false;

        if (this.fadeTimeout) {
            clearTimeout(this.fadeTimeout);
            this.fadeTimeout = null;
        }
    }

    // ============================================
    // ПУБЛИЧНЫЕ МЕТОДЫ
    // ============================================

    /**
     * Проверка, активен ли звук
     */
    public isActive(): boolean {
        return this.isPlaying && this.currentLevel > 0 && this.volume > 0;
    }

    /**
     * Получение текущей частоты
     */
    public getCurrentFrequency(): number {
        return this.currentFrequency;
    }

    /**
     * Получение текущего уровня
     */
    public getCurrentLevel(): number {
        return this.currentLevel;
    }

    /**
     * Полная остановка и освобождение ресурсов
     */
    public async dispose(): Promise<void> {
        this.stopSound();
        if (this.audioContext) {
            try {
                await this.audioContext.close();
            } catch (e) {}
            this.audioContext = null;
        }
        this.currentLevel = 0;
        this.currentFrequency = 800;
        this.volume = 0;
        console.log('[Audio] Ресурсы освобождены');
    }
}

export const audioEngine = new AudioEngine();