import 'dart:math';
import 'dart:ui';

import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:octo_image/octo_image.dart';
import 'package:simple_gesture_detector/simple_gesture_detector.dart';

import '../components/favourite_button.dart';
import '../services/finamp_settings_helper.dart';
import '../services/music_player_background_task.dart';
import '../models/jellyfin_models.dart';
import '../components/album_image.dart';
import '../components/PlayerScreen/song_name.dart';
import '../components/PlayerScreen/progress_slider.dart';
import '../components/PlayerScreen/player_buttons.dart';
import '../components/PlayerScreen/queue_button.dart';
import '../components/PlayerScreen/playback_mode.dart';
import '../components/PlayerScreen/add_to_playlist_button.dart';
import '../components/PlayerScreen/sleep_timer_button.dart';

final _albumImageProvider = StateProvider.autoDispose<ImageProvider?>(
  (_) => null,
);

class PlayerScreen extends StatelessWidget {
  const PlayerScreen({Key? key}) : super(key: key);

  static const routeName = "/nowplaying";

  @override
  Widget build(BuildContext context) {
    final audioHandler = GetIt.instance<MusicPlayerBackgroundTask>();

    return SimpleGestureDetector(
      onVerticalSwipe: (direction) {
        if (!FinampSettingsHelper.finampSettings.disableGesture &&
            direction == SwipeDirection.down) {
          Navigator.of(context).pop();
        }
      },
      onHorizontalSwipe: (direction) {
        if (!FinampSettingsHelper.finampSettings.disableGesture) {
          switch (direction) {
            case SwipeDirection.left:
              audioHandler.skipToNext();
              break;
            case SwipeDirection.right:
              audioHandler.skipToPrevious();
              break;
            default:
              break;
          }
        }
      },
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          elevation: 0,
          actions: const [SleepTimerButton(), AddToPlaylistButton()],
        ),
        // Required for sleep timer input
        resizeToAvoidBottomInset: false,
        extendBodyBehindAppBar: true,
        body: Stack(
          children: [
            if (FinampSettingsHelper.finampSettings.showCoverAsPlayerBackground)
              const _BlurredPlayerScreenBackground(),
            SafeArea(
              child: LayoutBuilder(
                builder: (context, constraints) {
                  final isWide = constraints.maxWidth >= 720;
                  final horizontalPadding = isWide ? 40.0 : 20.0;

                  return Padding(
                    padding: EdgeInsets.fromLTRB(
                      horizontalPadding,
                      16,
                      horizontalPadding,
                      24,
                    ),
                    child: isWide
                        ? Row(
                            children: const [
                              Expanded(flex: 5, child: _PlayerArtworkSurface()),
                              SizedBox(width: 32),
                              Expanded(
                                flex: 4,
                                child: _PlayerControlsSurface(),
                              ),
                            ],
                          )
                        : const Column(
                            children: [
                              Expanded(flex: 6, child: _PlayerArtworkSurface()),
                              SizedBox(height: 18),
                              Expanded(
                                flex: 5,
                                child: _PlayerControlsSurface(),
                              ),
                            ],
                          ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _PlayerArtworkSurface extends StatelessWidget {
  const _PlayerArtworkSurface({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Center(
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 460),
        child: DecoratedBox(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(36),
            boxShadow: [
              BoxShadow(
                color: colorScheme.shadow.withOpacity(0.24),
                blurRadius: 36,
                offset: const Offset(0, 20),
              ),
            ],
          ),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(36),
            child: ColoredBox(
              color: colorScheme.surfaceVariant.withOpacity(0.34),
              child: const Padding(
                padding: EdgeInsets.all(8),
                child: ClipRRect(
                  borderRadius: BorderRadius.all(Radius.circular(28)),
                  child: _PlayerScreenAlbumImage(),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _PlayerControlsSurface extends StatelessWidget {
  const _PlayerControlsSurface({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    final surfaceColor = Color.alphaBlend(
      colorScheme.primary.withOpacity(0.07),
      colorScheme.surface,
    );

    return Center(
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 560),
        child: DecoratedBox(
          decoration: BoxDecoration(
            color: surfaceColor,
            borderRadius: BorderRadius.circular(32),
            border: Border.all(
              color: colorScheme.outlineVariant.withOpacity(0.45),
            ),
            boxShadow: [
              BoxShadow(
                color: colorScheme.shadow.withOpacity(0.12),
                blurRadius: 28,
                offset: const Offset(0, 14),
              ),
            ],
          ),
          child: const Padding(
            padding: EdgeInsets.fromLTRB(22, 24, 22, 18),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                SongName(),
                SizedBox(height: 24),
                ProgressSlider(),
                SizedBox(height: 18),
                PlayerButtons(),
                SizedBox(height: 18),
                _PlayerSecondaryActions(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _PlayerSecondaryActions extends StatelessWidget {
  const _PlayerSecondaryActions({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Row(
      children: [
        Expanded(child: PlaybackMode()),
        _PlayerScreenFavoriteButton(),
        SizedBox(width: 8),
        QueueButton(),
      ],
    );
  }
}

/// This widget is just an AlbumImage in a StreamBuilder to get the song id.
class _PlayerScreenAlbumImage extends ConsumerWidget {
  const _PlayerScreenAlbumImage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final audioHandler = GetIt.instance<MusicPlayerBackgroundTask>();

    return StreamBuilder<MediaItem?>(
      stream: audioHandler.mediaItem,
      builder: (context, snapshot) {
        final item = snapshot.data?.extras?["itemJson"] == null
            ? null
            : BaseItemDto.fromJson(snapshot.data!.extras!["itemJson"]);

        return item == null
            ? AspectRatio(
                aspectRatio: 1,
                child: ClipRRect(
                  borderRadius: AlbumImage.borderRadius,
                  child: Container(color: Theme.of(context).cardColor),
                ),
              )
            : AlbumImage(
                item: item,
                imageProviderCallback: (imageProvider) =>
                    // We need a post frame callback because otherwise this
                    // widget rebuilds on the same frame
                    WidgetsBinding.instance.addPostFrameCallback(
                      (_) => ref.read(_albumImageProvider.notifier).state =
                          imageProvider,
                    ),
                // Here we awkwardly get the next 3 queue items so that we
                // can precache them (so that the image is already loaded
                // when the next song comes on).
                itemsToPrecache: audioHandler.queue.value
                    .sublist(
                      min(
                        (audioHandler.playbackState.value.queueIndex ?? 0) + 1,
                        audioHandler.queue.value.length,
                      ),
                    )
                    .take(3)
                    .map((e) => BaseItemDto.fromJson(e.extras!["itemJson"]))
                    .toList(),
              );
      },
    );
  }
}

/// Same as [_PlayerScreenAlbumImage], but with a BlurHash instead. We also
/// filter the BlurHash so that it works as a background image.
class _BlurredPlayerScreenBackground extends ConsumerWidget {
  const _BlurredPlayerScreenBackground({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final imageProvider = ref.watch(_albumImageProvider);

    return ClipRect(
      child: imageProvider == null
          ? const SizedBox.shrink()
          : OctoImage(
              image: imageProvider,
              fit: BoxFit.cover,
              placeholderBuilder: (_) => const SizedBox.shrink(),
              errorBuilder: (_, __, ___) => const SizedBox.shrink(),
              imageBuilder: (context, child) => ColorFiltered(
                colorFilter: ColorFilter.mode(
                  Theme.of(context).brightness == Brightness.dark
                      ? Colors.black.withOpacity(0.35)
                      : Colors.white.withOpacity(0.75),
                  BlendMode.srcOver,
                ),
                child: ImageFiltered(
                  imageFilter: ImageFilter.blur(
                    sigmaX: 85,
                    sigmaY: 85,
                    tileMode: TileMode.mirror,
                  ),
                  child: SizedBox.expand(child: child),
                ),
              ),
            ),
    );
  }
}

class _PlayerScreenFavoriteButton extends StatelessWidget {
  const _PlayerScreenFavoriteButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final audioHandler = GetIt.instance<MusicPlayerBackgroundTask>();

    return StreamBuilder<MediaItem?>(
      stream: audioHandler.mediaItem,
      builder: (context, snapshot) {
        return FavoriteButton(
          item: snapshot.data?.extras?["itemJson"] == null
              ? null
              : BaseItemDto.fromJson(snapshot.data!.extras!["itemJson"]),
          inPlayer: true,
        );
      },
    );
  }
}
