import 'dart:async';

import 'package:aves/model/settings/settings.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/theme/format.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:aves/widgets/common/fx/blurred.dart';
import 'package:aves/widgets/common/fx/borders.dart';
import 'package:aves_video/aves_video.dart';
import 'package:decorated_icon/decorated_icon.dart';
import 'package:flutter/material.dart';

class VideoProgressBar extends StatefulWidget {
  final AvesVideoController? controller;
  final Animation<double> scale;

  const VideoProgressBar({
    super.key,
    required this.controller,
    required this.scale,
  });

  @override
  State<VideoProgressBar> createState() => _VideoProgressBarState();
}

class _VideoProgressBarState extends State<VideoProgressBar> {
  final GlobalKey _progressBarKey = GlobalKey(debugLabel: 'video-progress-bar');
  bool _playingOnDragStart = false;

  static const double radius = 123;

  AvesVideoController? get controller => widget.controller;

  Stream<int> get positionStream => controller?.positionStream ?? Stream.value(0);

  bool get isPlaying => controller?.isPlaying ?? false;

  ValueNotifier<ABRepeat?> get abRepeatNotifier => controller?.abRepeatNotifier ?? ValueNotifier(null);

  @override
  Widget build(BuildContext context) {
    final blurred = settings.enableBlurEffect;
    final theme = Theme.of(context);
    return SizeTransition(
      sizeFactor: widget.scale,
      child: BlurredRRect.all(
        enabled: blurred,
        borderRadius: radius,
        child: GestureDetector(
          onTapDown: (details) {
            _seekFromTap(details.globalPosition);
          },
          onHorizontalDragStart: (details) {
            _playingOnDragStart = isPlaying;
            if (_playingOnDragStart) controller?.pause();
          },
          onHorizontalDragUpdate: (details) {
            _seekFromTap(details.globalPosition);
          },
          onHorizontalDragEnd: (details) {
            if (_playingOnDragStart) controller?.play();
          },
          child: ConstrainedBox(
            constraints: const BoxConstraints(minHeight: kMinInteractiveDimension),
            child: Container(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              decoration: BoxDecoration(
                color: Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred),
                border: AvesBorder.border(context),
                borderRadius: const BorderRadius.all(Radius.circular(radius)),
              ),
              child: MediaQuery(
                data: MediaQuery.of(context).copyWith(
                  textScaler: TextScaler.noScaling,
                ),
                child: ValueListenableBuilder<ABRepeat?>(
                  valueListenable: abRepeatNotifier,
                  builder: (context, abRepeat, child) {
                    return Stack(
                      fit: StackFit.passthrough,
                      children: [
                        if (abRepeat != null) ...[
                          _buildABRepeatMark(context, abRepeat.start),
                          _buildABRepeatMark(context, abRepeat.end),
                        ],
                        Container(
                          key: _progressBarKey,
                          alignment: Alignment.center,
                          padding: const EdgeInsets.symmetric(vertical: 4),
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Row(
                                children: [
                                  StreamBuilder<int>(
                                    stream: positionStream,
                                    builder: (context, snapshot) {
                                      // do not use stream snapshot because it is obsolete when switching between videos
                                      final position = controller?.currentPosition.floor() ?? 0;
                                      return _buildText(formatFriendlyDuration(Duration(milliseconds: position)));
                                    },
                                  ),
                                  const Spacer(),
                                  _buildText(formatFriendlyDuration(Duration(milliseconds: controller?.duration ?? 0))),
                                ],
                              ),
                              ClipRRect(
                                borderRadius: const BorderRadius.all(Radius.circular(4)),
                                child: Directionality(
                                  textDirection: videoPlaybackDirection,
                                  child: StreamBuilder<int>(
                                    stream: positionStream,
                                    builder: (context, snapshot) {
                                      // do not use stream snapshot because it is obsolete when switching between videos
                                      var progress = controller?.progress ?? 0.0;
                                      if (!progress.isFinite) progress = 0.0;
                                      return LinearProgressIndicator(
                                        value: progress,
                                        backgroundColor: theme.colorScheme.onSurface.withValues(alpha: .2),
                                      );
                                    },
                                  ),
                                ),
                              ),
                              Row(
                                children: [
                                  _buildSpeedIndicator(),
                                  _buildMuteIndicator(),
                                  // fake text below to match the height of the text above and center the whole thing
                                  _buildText(''),
                                ],
                              ),
                            ],
                          ),
                        ),
                      ],
                    );
                  },
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildText(String text) {
    return Text(
      text,
      style: TextStyle(
        shadows: Theme.of(context).isDark ? AStyles.embossShadows : null,
      ),
      strutStyle: const StrutStyle(
        forceStrutHeight: true,
      ),
    );
  }

  Widget _buildABRepeatMark(BuildContext context, int? position) {
    if (controller == null || position == null) return const SizedBox();
    return Positioned(
      left: _progressToDx(position / controller!.duration),
      top: 0,
      bottom: 0,
      child: Container(
        decoration: BoxDecoration(
          border: Border(left: AvesBorder.straightSide(context, width: 2)),
        ),
      ),
    );
  }

  Widget _buildSpeedIndicator() => StreamBuilder<double>(
    stream: controller?.speedStream ?? Stream.value(1.0),
    builder: (context, snapshot) {
      final speed = controller?.speed ?? 1.0;
      return speed != 1
          ? Padding(
              padding: const EdgeInsetsDirectional.only(end: 8),
              child: _buildText('x$speed'),
            )
          : const SizedBox();
    },
  );

  Widget _buildMuteIndicator() => StreamBuilder<double>(
    stream: controller?.volumeStream ?? Stream.value(1.0),
    builder: (context, snapshot) {
      final textScaler = MediaQuery.textScalerOf(context);
      final isMuted = controller?.isMuted ?? false;
      return isMuted
          ? Padding(
              padding: const EdgeInsetsDirectional.only(end: 8),
              child: DecoratedIcon(
                AIcons.mute,
                size: textScaler.scale(16),
                shadows: Theme.of(context).isDark ? AStyles.embossShadows : null,
              ),
            )
          : const SizedBox();
    },
  );

  RenderBox? _getProgressBarRenderBox() {
    return _progressBarKey.currentContext?.findRenderObject() as RenderBox?;
  }

  void _seekFromTap(Offset globalPosition) async {
    final box = _getProgressBarRenderBox();
    if (controller == null || box == null) return;

    final dx = box.globalToLocal(globalPosition).dx;
    await controller!.seekToProgress(dx / box.size.width);
  }

  double? _progressToDx(double progress) {
    final box = _getProgressBarRenderBox();
    return box != null && box.hasSize ? progress * box.size.width : null;
  }
}
