import 'package:audioplayers/audioplayers.dart';

/// Looping background music, independent of the SFX pool in [AudioService].
/// One track plays at a time from `assets/audio/bgm/<key>.mp3`. A track change
/// (e.g. switching seasons) restarts; toggling sound off or backgrounding the
/// app pauses/resumes the same track. Every player call is swallowed — music
/// must never break the app, and a missing track asset is silently ignored.
class MusicService {
  MusicService({bool enabled = true}) : _enabled = enabled;

  /// Background music sits well under the SFX so it never competes.
  static const double _volume = 0.45;

  final AudioPlayer _player = AudioPlayer();
  bool _enabled;
  bool _backgrounded = false;
  String? _currentKey;

  bool get enabled => _enabled;
  bool get _shouldPlay => _enabled && !_backgrounded && _currentKey != null;

  /// Request a looping track by theme key (e.g. 'menu', 'season_001'). No-op
  /// if it is already the current track (avoids restarting on every rebuild).
  Future<void> playKey(String key) async {
    if (key == _currentKey) {
      await _reconcile();
      return;
    }
    _currentKey = key;
    await _restart();
  }

  set enabled(bool value) {
    if (_enabled == value) return;
    _enabled = value;
    _reconcile();
  }

  /// App lifecycle: pause when backgrounded, resume on return.
  void setBackgrounded(bool value) {
    if (_backgrounded == value) return;
    _backgrounded = value;
    _reconcile();
  }

  Future<void> _restart() async {
    try {
      await _player.stop();
      if (!_shouldPlay) return;
      await _player.setReleaseMode(ReleaseMode.loop);
      await _player.play(
        AssetSource('audio/bgm/$_currentKey.mp3'),
        volume: _volume,
      );
    } catch (_) {
      // Missing asset or platform hiccup: stay silent.
    }
  }

  /// Pause/resume the CURRENT track to match [_shouldPlay] without restarting.
  Future<void> _reconcile() async {
    try {
      if (_shouldPlay) {
        if (_player.state == PlayerState.paused) {
          await _player.resume();
        } else if (_player.state != PlayerState.playing) {
          await _restart();
        }
      } else if (_player.state == PlayerState.playing) {
        await _player.pause();
      }
    } catch (_) {
      // Never throw from lifecycle/toggle changes.
    }
  }

  void dispose() => _player.dispose();
}
