import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:spotify_sdk/models/image_uri.dart';
import 'package:spotify_sdk/models/player_state.dart';
import 'package:spotify_sdk/spotify_sdk.dart';

/// InheritedWidget that holds the state of your MusicPlayer
/// the rebuild stream builds the hole body
/// the playStatedStream only rebuild the Image, SongInfo and the ControlsButtons
class MusicPlayerState extends InheritedWidget {
  MusicPlayerState({super.key, required super.child});

  bool loading = false;
  bool connected = false;
  StreamController<void> rebuildStream = StreamController.broadcast();
  StreamController<void> playStatedStream = StreamController.broadcast();
  PlayerState? playerState;
  late ImageUri? currentTrackImageUri;
  Image? albumImage;

  static MusicPlayerState? maybeOf(BuildContext context) =>
      context.dependOnInheritedWidgetOfExactType<MusicPlayerState>();

  static MusicPlayerState of(BuildContext context) {
    final MusicPlayerState? result = maybeOf(context);
    assert(result != null, 'No PlayerState found in context');
    return result!;
  }

  void setLoading(bool loading) {
    this.loading = loading;
    rebuildStream.sink.add(null);
  }

  /// will be called on PlayStateChange
  /// load new Imag e
  void updatePlayerState() async {
    playerState = await getPlayerState();
    loadAlbumImage().then((value) => playStatedStream.sink.add(null));
  }

  ///disconnect your app from your local spotify app
  Future<void> disconnect() async {
    try {
      setLoading(true);
      var result = await SpotifySdk.disconnect();
      setStatus(result ? 'disconnect successful' : 'disconnect failed');
      setLoading(false);
    } on PlatformException catch (e) {
      setLoading(false);
      setStatus(e.code, message: e.message);
    } on MissingPluginException {
      setLoading(false);
      setStatus('not implemented');
    }
  }

  /// establish connection to local spotify app
  /// connection data will be loaded in main from dotenv.env
  /// if connection successful
  /// connection will be set to true and loading to false
  Future<bool> connectToSpotifyRemote() async {
    try {
      setLoading(true);
      var result = await SpotifySdk.connectToSpotifyRemote(
          clientId: dotenv.env['CLIENT_ID'].toString(),
          redirectUrl: dotenv.env['REDIRECT_URL'].toString());
      setStatus(result
          ? 'connect to spotify successful'
          : 'connect to spotify failed');
      if (result) {
        playerState = await getPlayerState();
        setLoading(false);
      }
      connected = result;
    } on PlatformException catch (e) {
      setLoading(false);
      setStatus(e.code, message: e.message);
    } on MissingPluginException {
      setLoading(false);
      setStatus('not implemented');
    }
    return connected;
  }

  Widget spotifyImageWidget(ImageUri imageUri) {
    return FutureBuilder(
        future: SpotifySdk.getImage(
          imageUri: imageUri,
          dimension: ImageDimension.large,
        ),
        builder: (BuildContext context, AsyncSnapshot<Uint8List?> snapshot) {
          if (snapshot.hasData) {
            albumImage = Image.memory(snapshot.data!);
            rebuildStream.sink.add(null);
            return const Center(child: Text('Getting image...'));
          } else if (snapshot.hasError) {
            setStatus(snapshot.error.toString());
            return SizedBox(
              width: ImageDimension.small.value.toDouble(),
              height: ImageDimension.small.value.toDouble(),
              child: const Center(child: Text('Error getting image')),
            );
          } else {
            return SizedBox(
              width: ImageDimension.small.value.toDouble(),
              height: ImageDimension.small.value.toDouble(),
              child: const Center(child: Text('Getting image...')),
            );
          }
        });
  }


  ///loads the Album image via spotify.sdk and saves the image in MusicPlayState
  ///for global access
  Future<void> loadAlbumImage() async {
    currentTrackImageUri = playerState?.track?.imageUri;
    albumImage = Image.memory((await SpotifySdk.getImage(
      imageUri: currentTrackImageUri!,
      dimension: ImageDimension.large,
    ))!);
  }

  void setStatus(String code, {String? message}) {
    var text = message ?? '';
    print(text);
  }

  //get PlayState from local spotify app
  Future<PlayerState?> getPlayerState() async {
    try {
      return await SpotifySdk.getPlayerState();
    } on PlatformException catch (e) {
      setStatus(e.code, message: e.message);
    } on MissingPluginException {
      setStatus('not implemented');
    }
  }

  @override
  bool updateShouldNotify(covariant MusicPlayerState oldWidget) {
    return loading != oldWidget.loading && connected != oldWidget.connected;
  }
}