import 'dart:async';
import 'dart:developer';
import 'package:get/get.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:rhythm_box/providers/audio_player.dart';
import 'package:rhythm_box/providers/error_notifier.dart';
import 'package:rhythm_box/providers/history.dart';
import 'package:rhythm_box/providers/palette.dart';
import 'package:rhythm_box/providers/scrobbler.dart';
import 'package:rhythm_box/providers/skip_segments.dart';
import 'package:rhythm_box/providers/user_preferences.dart';
import 'package:rhythm_box/services/audio_player/audio_player.dart';
import 'package:rhythm_box/services/audio_services/audio_services.dart';
import 'package:rhythm_box/services/audio_services/image.dart';
import 'package:rhythm_box/services/local_track.dart';
import 'package:rhythm_box/services/server/sourced_track.dart';
import 'package:rhythm_box/widgets/auto_cache_image.dart';

class AudioPlayerStreamProvider extends GetxController {
  late final AudioServices notificationService;

  List<StreamSubscription>? _subscriptions;

  AudioPlayerStreamProvider() {
    _initNotificationService();
  }

  Future<void> _initNotificationService() async {
    try {
      final res = await AudioServices.create();
      notificationService = res;
    } catch (err) {
      log('[AudioService] Unable to init audio service: $err');
    }
  }

  @override
  void onInit() {
    super.onInit();

    _subscriptions = [
      subscribeToPlaylist(),
      subscribeToSkipSponsor(),
      subscribeToScrobbleChanged(),
      subscribeToPosition(),
      subscribeToPlayerError(),
    ];
  }

  @override
  void dispose() {
    if (_subscriptions != null) {
      for (final subscription in _subscriptions!) {
        subscription.cancel();
      }
    }
    super.dispose();
  }

  Future<void> updatePalette() async {
    if (!Get.find<UserPreferencesProvider>().state.value.albumColorSync) {
      Get.find<PaletteProvider>().clear();
      return;
    }

    final AudioPlayerProvider playback = Get.find();
    final activeTrack = playback.state.value.activeTrack;
    if (activeTrack == null) return;

    if (activeTrack.album?.images != null) {
      final newPalette = await PaletteGenerator.fromImageProvider(
        AutoCacheImage.provider(
          (activeTrack.album?.images).asUrlString()!,
        ),
      );
      Get.find<PaletteProvider>().updatePalette(newPalette);
    }
  }

  StreamSubscription subscribeToPlaylist() {
    final AudioPlayerProvider playback = Get.find();
    return audioPlayer.playlistStream.listen((mpvPlaylist) {
      final activeTrack = playback.state.value.activeTrack;
      if (activeTrack != null) {
        notificationService.addTrack(activeTrack);
        updatePalette();
      }
    });
  }

  StreamSubscription subscribeToSkipSponsor() {
    return audioPlayer.positionStream.listen((position) async {
      final currentSegments =
          await Get.find<SegmentsProvider>().fetchSegments();

      if (currentSegments?.segments.isNotEmpty != true ||
          position < const Duration(seconds: 3)) return;

      for (final segment in currentSegments!.segments) {
        final seconds = position.inSeconds;

        if (seconds < segment.start || seconds >= segment.end) continue;

        await audioPlayer.seek(Duration(seconds: segment.end + 1));
      }
    });
  }

  StreamSubscription subscribeToScrobbleChanged() {
    String? lastScrobbled;
    return audioPlayer.positionStream.listen((position) {
      try {
        final AudioPlayerProvider playback = Get.find();
        final uid = playback.state.value.activeTrack is LocalTrack
            ? (playback.state.value.activeTrack as LocalTrack).path
            : playback.state.value.activeTrack?.id;

        if (playback.state.value.activeTrack == null ||
            lastScrobbled == uid ||
            position.inSeconds < 30) {
          return;
        }

        Get.find<ScrobblerProvider>()
            .scrobble(playback.state.value.activeTrack!);
        Get.find<PlaybackHistoryProvider>()
            .addTrack(playback.state.value.activeTrack!);
        lastScrobbled = uid;
      } catch (e, stack) {
        Get.find<ErrorNotifier>()
            .logError('[Scrobbler] Error: $e', trace: stack);
      }
    });
  }

  StreamSubscription subscribeToPosition() {
    String lastTrack = ''; // used to prevent multiple calls to the same track
    final AudioPlayerProvider playback = Get.find();
    return audioPlayer.positionStream.listen((event) async {
      if (event < const Duration(seconds: 3) ||
          audioPlayer.playlist.index == -1 ||
          audioPlayer.playlist.index ==
              playback.state.value.tracks.length - 1) {
        return;
      }
      final nextTrack = RhythmMedia.fromMedia(
        audioPlayer.playlist.medias.elementAt(audioPlayer.playlist.index + 1),
      );

      if (lastTrack == nextTrack.track.id || nextTrack.track is LocalTrack) {
        return;
      }

      try {
        await Get.find<SourcedTrackProvider>().fetch(nextTrack);
      } finally {
        lastTrack = nextTrack.track.id!;
      }
    });
  }

  StreamSubscription subscribeToPlayerError() {
    return audioPlayer.errorStream.listen((event) {
      // Handle player error events here
    });
  }
}
