import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';

import '../models/track.dart';

class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler {
  MinstrelAudioHandler() {
    _player.playbackEventStream.listen(_broadcastState);
  }

  final AudioPlayer _player = AudioPlayer();
  String _baseUrl = '';
  String? _token;

  void configure({required String baseUrl, required String? token}) {
    _baseUrl = baseUrl;
    _token = token;
  }

  Future<void> setQueueFromTracks(List<TrackRef> tracks, {int initialIndex = 0}) async {
    final items = tracks.map(_toMediaItem).toList();
    queue.add(items);
    if (items.isNotEmpty) {
      mediaItem.add(items[initialIndex.clamp(0, items.length - 1)]);
    }

    final headers = _token == null ? null : {'Authorization': 'Bearer $_token'};
    final sources = tracks.map((t) {
      final url = t.streamUrl.isNotEmpty
          ? (Uri.tryParse(t.streamUrl)?.hasScheme == true
              ? t.streamUrl
              : '$_baseUrl${t.streamUrl}')
          : '$_baseUrl/api/tracks/${t.id}/stream';
      return AudioSource.uri(Uri.parse(url), headers: headers);
    }).toList();

    await _player.setAudioSource(
      ConcatenatingAudioSource(children: sources),
      initialIndex: initialIndex,
    );
  }

  MediaItem _toMediaItem(TrackRef t) => MediaItem(
        id: t.id,
        title: t.title,
        artist: t.artistName,
        album: t.albumTitle,
        duration: Duration(seconds: t.durationSec),
      );

  @override
  Future<void> play() => _player.play();

  @override
  Future<void> pause() => _player.pause();

  @override
  Future<void> seek(Duration position) => _player.seek(position);

  @override
  Future<void> skipToNext() => _player.seekToNext();

  @override
  Future<void> skipToPrevious() => _player.seekToPrevious();

  void _broadcastState(PlaybackEvent event) {
    final playing = _player.playing;
    playbackState.add(PlaybackState(
      controls: [
        MediaControl.skipToPrevious,
        if (playing) MediaControl.pause else MediaControl.play,
        MediaControl.skipToNext,
      ],
      systemActions: const {MediaAction.seek},
      processingState: switch (_player.processingState) {
        ProcessingState.idle => AudioProcessingState.idle,
        ProcessingState.loading => AudioProcessingState.loading,
        ProcessingState.buffering => AudioProcessingState.buffering,
        ProcessingState.ready => AudioProcessingState.ready,
        ProcessingState.completed => AudioProcessingState.completed,
      },
      playing: playing,
      updatePosition: _player.position,
      bufferedPosition: _player.bufferedPosition,
      speed: _player.speed,
      queueIndex: event.currentIndex,
    ));
  }
}
