import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';

enum SfxKey { win, loss, select, drop, shuffle }

enum BgmKey { bgm }

class MusicMap {
  static const String bgm = 'assets/audio/bgm.mp3';
  static const Map<SfxKey, String> sfx = {
    SfxKey.win: 'assets/audio/win.mp3',
    SfxKey.loss: 'assets/audio/loss.mp3',
    SfxKey.shuffle: 'assets/audio/shuffle.mp3',
    SfxKey.select: 'assets/audio/select.mp3',
    SfxKey.drop: 'assets/audio/drop.mp3',
  };
}

class AudioManager {
  static final AudioManager instance = AudioManager._();
  AudioManager._();

  late final AudioPlayer _bgm = AudioPlayer(playerId: 'bgm');

  final List<AudioPlayer> _sfxPlayers = [];
  int _sfxIndex = 0;

  bool _initialized = false;
  bool _initializing = false;

  bool _bgmMuted = false;
  bool _sfxMuted = false;

  double _bgmVolume = 0.6;
  double _sfxVolume = 1.0;

  static const int _sfxPoolSize = 2;

  Future<void> init() async {
    if (_initialized || _initializing) return;
    _initializing = true;

    try {
      _sfxPlayers.clear();
      _sfxIndex = 0;

      await _initBgmPlayer();
      await _initSfxPlayers();

      if (_sfxPlayers.isEmpty) {
        final fallback = AudioPlayer(playerId: 'sfx_fallback');
        await _safeSetSfxPlayerDefaults(fallback);
        _sfxPlayers.add(fallback);
      }

      _initialized = true;
    } catch (e, st) {
      debugPrint('AudioManager init failed: $e');
      debugPrint('$st');
      _initialized = false;
      _sfxPlayers.clear();
      _sfxIndex = 0;
    } finally {
      _initializing = false;
    }
  }

  Future<void> _initBgmPlayer() async {
    try {
      await _bgm.setReleaseMode(ReleaseMode.loop);
    } catch (e) {
      debugPrint('BGM setReleaseMode failed: $e');
    }

    try {
      await _bgm.setPlayerMode(PlayerMode.mediaPlayer);
    } catch (e) {
      debugPrint('BGM setPlayerMode failed: $e');
    }

    try {
      await _bgm.setAudioContext(
        AudioContext(
          android: AudioContextAndroid(
            audioFocus: AndroidAudioFocus.gain,
            contentType: AndroidContentType.music,
            usageType: AndroidUsageType.media,
            audioMode: AndroidAudioMode.normal,
            isSpeakerphoneOn: false,
            stayAwake: false,
          ),
          iOS: AudioContextIOS(
            category: AVAudioSessionCategory.playback,
            options: const {AVAudioSessionOptions.mixWithOthers},
          ),
        ),
      );
    } catch (e) {
      debugPrint('BGM setAudioContext failed: $e');
    }

    try {
      await _bgm.setVolume(_bgmVolume);
    } catch (e) {
      debugPrint('BGM setVolume failed: $e');
    }
  }

  Future<void> _initSfxPlayers() async {
    for (int i = 0; i < _sfxPoolSize; i++) {
      final player = AudioPlayer(playerId: 'sfx_$i');

      await _safeSetSfxPlayerDefaults(player, index: i);

      _sfxPlayers.add(player);
    }
  }

  Future<void> _safeSetSfxPlayerDefaults(
    AudioPlayer player, {
    int? index,
  }) async {
    final tag = index == null ? 'SFX[fallback]' : 'SFX[$index]';

    try {
      await player.setReleaseMode(ReleaseMode.stop);
    } catch (e) {
      debugPrint('$tag setReleaseMode failed: $e');
    }

    try {
      await player.setPlayerMode(PlayerMode.lowLatency);
    } catch (e) {
      debugPrint('$tag lowLatency failed, fallback to mediaPlayer: $e');
      try {
        await player.setPlayerMode(PlayerMode.mediaPlayer);
      } catch (e2) {
        debugPrint('$tag mediaPlayer fallback failed: $e2');
      }
    }

    try {
      await player.setAudioContext(
        AudioContext(
          android: AudioContextAndroid(
            audioFocus: AndroidAudioFocus.none,
            contentType: AndroidContentType.sonification,
            usageType: AndroidUsageType.game,
            audioMode: AndroidAudioMode.normal,
            isSpeakerphoneOn: false,
            stayAwake: false,
          ),
          iOS: AudioContextIOS(
            category: AVAudioSessionCategory.playback,
            options: const {AVAudioSessionOptions.mixWithOthers},
          ),
        ),
      );
    } catch (e) {
      debugPrint('$tag setAudioContext failed: $e');
    }

    try {
      await player.setVolume(_sfxVolume);
    } catch (e) {
      debugPrint('$tag setVolume failed: $e');
    }
  }

  Future<void> _ensureInit() async {
    if (!_initialized) {
      await init();
    }
    if (_sfxPlayers.isEmpty) {
      final fallback = AudioPlayer(playerId: 'sfx_emergency');
      await _safeSetSfxPlayerDefaults(fallback);
      _sfxPlayers.add(fallback);
    }
  }

  AudioPlayer _nextSfxPlayer() {
    if (_sfxPlayers.isEmpty) {
      throw StateError('SFX players not initialized');
    }
    final player = _sfxPlayers[_sfxIndex % _sfxPlayers.length];
    _sfxIndex = (_sfxIndex + 1) % _sfxPlayers.length;
    return player;
  }

  Future<void> setBgmMuted(bool muted) async {
    await _ensureInit();
    _bgmMuted = muted;
    if (muted) {
      await _bgm.pause();
    } else {
      await _bgm.resume();
    }
  }

  Future<void> setSfxMuted(bool muted) async {
    await _ensureInit();
    _sfxMuted = muted;
    if (muted) {
      for (final p in _sfxPlayers) {
        try {
          await p.stop();
        } catch (_) {}
      }
    }
  }

  Future<void> setBgmVolume(double value) async {
    await _ensureInit();
    _bgmVolume = value.clamp(0.0, 1.0);
    await _bgm.setVolume(_bgmVolume);
  }

  Future<void> setSfxVolume(double value) async {
    await _ensureInit();
    _sfxVolume = value.clamp(0.0, 1.0);
    for (final p in _sfxPlayers) {
      try {
        await p.setVolume(_sfxVolume);
      } catch (_) {}
    }
  }

  Future<void> playBgm([BgmKey key = BgmKey.bgm]) async {
    await _ensureInit();
    if (_bgmMuted) return;

    final path = MusicMap.bgm.replaceFirst('assets/', '');
    await _bgm.stop();
    await _bgm.play(AssetSource(path), volume: _bgmVolume);
  }

  Future<void> pauseBgm() async {
    if (!_initialized) return;
    await _bgm.pause();
  }

  Future<void> resumeBgm() async {
    await _ensureInit();
    if (_bgmMuted) return;
    await _bgm.resume();
  }

  Future<void> stopBgm() async {
    if (!_initialized) return;
    await _bgm.stop();
  }

  Future<void> playSfx(SfxKey key) async {
    await _ensureInit();
    if (_sfxMuted) return;
    if (_sfxPlayers.isEmpty) return;

    final path = MusicMap.sfx[key]!.replaceFirst('assets/', '');
    final player = _nextSfxPlayer();

    try {
      await player.stop();
    } catch (_) {}

    try {
      await player.setVolume(_sfxVolume);
    } catch (_) {}

    try {
      await player.play(AssetSource(path), volume: _sfxVolume);
    } catch (e) {
      debugPrint('playSfx failed: $e');
    }
  }

  Future<void> stopAllSfx() async {
    if (!_initialized) return;
    for (final p in _sfxPlayers) {
      try {
        await p.stop();
      } catch (_) {}
    }
  }

  Future<void> releaseAll() async {
    try {
      await _bgm.dispose();
    } catch (_) {}

    for (final p in _sfxPlayers) {
      try {
        await p.dispose();
      } catch (_) {}
    }

    _sfxPlayers.clear();
    _sfxIndex = 0;
    _initialized = false;
    _initializing = false;
  }
}
