import 'dart:async';
import 'dart:ui';

import 'package:design/design.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:guru_widgets/animation/lottie/lottie_widget.dart';
import 'package:guru_widgets/button/single_tap_widget.dart';
import 'package:guru_widgets/common/divider.dart';
import 'package:guru_widgets/common/flexible_container.dart';
import 'package:guru_widgets/common/spacer.dart';
import 'package:guru_widgets/guru_widgets.dart';
import 'package:guru_widgets/localizations/widgets_strings.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_widgets/theme/guru_theme.dart';
import 'package:guru_utils/tuple/tuple.dart';
import 'package:lottie/lottie.dart';
import 'package:rxdart/rxdart.dart';

/// Created by Haoyi on 2023/9/5
///
part 'guru_loading.g.dart';

@DesignSpec(width: 750, height: 1624)
abstract class GuruLoadingDesignSpec implements BasicDesignSpec {
  @SpecRadius.circular(SpecHeight(40))
  BorderRadius get radius;

  @SpecRadius.only(topStart: SpecHeight(40), topEnd: SpecHeight(40))
  BorderRadius get topRadius;

  @SpecRadius.only(bottomStart: SpecHeight(40), bottomEnd: SpecHeight(40))
  BorderRadius get bottomRadius;

  @NestedAspectWidthSpec(264, 0.83333333)
  GuruSimpleLoadingDesignSpec get simpleLoadingDesignSpec;

  @NestedAspectWidthSpec(448, 0.62053571)
  GuruCancellableLoadingDesignSpec get cancellableLoadingDesignSpec;

  @NestedAspectWidthSpec(400, 0.55)
  GuruFeedbackLoadingDesignSpec get feedbackLoadingDesignSpec;

  static GuruLoadingDesignSpec get() => _GuruLoadingDesignSpec.get();
}

@DesignSpec(width: 264, height: 220, specMode: SpecMode.nested)
abstract class GuruSimpleLoadingDesignSpec implements BasicDesignSpec {
  @SpecEdgeInsets.symmetric(
      horizontal: SpecHorizontal(40), vertical: SpecVertical(46))
  EdgeInsets get contentPadding;

  @SpecAbsoluteFontSize(24)
  double get loadingFontSize;

  @SpecHeight(72)
  double get loadingIconSize;

  @SpecHeight(36)
  double get dotSize;

  @SpecHeight(26)
  double get dotBottomSpacing;

  static GuruSimpleLoadingDesignSpec create(Size size,
          {Offset offset = Offset.zero}) =>
      _GuruSimpleLoadingDesignSpec.from(size, offset: offset);
}

@DesignSpec(width: 448, height: 278, specMode: SpecMode.nested)
abstract class GuruCancellableLoadingDesignSpec implements BasicDesignSpec {
  @SpecAbsoluteFontSize(24)
  double get loadingFontSize;

  @SpecHeight(72)
  double get loadingIconSize;

  @SpecHeight(8)
  double get loadingIconBottomSpacing;

  @SpecEdgeInsets.symmetric(
      horizontal: SpecHorizontal(40), vertical: SpecVertical(36))
  EdgeInsets get contentPadding;

  @SpecHeight(182)
  double get contentMinHeight;

  @SpecHeight(1)
  double get dividerSize;

  @SpecAbsoluteFontSize(32)
  double get cancelFontSize;

  @SpecHeight(96)
  double get cancelButtonHeight;

  static GuruCancellableLoadingDesignSpec create(Size size,
          {Offset offset = Offset.zero}) =>
      _GuruCancellableLoadingDesignSpec.from(size, offset: offset);
}

@DesignSpec(width: 400, height: 220, specMode: SpecMode.nested)
abstract class GuruFeedbackLoadingDesignSpec implements BasicDesignSpec {
  @SpecEdgeInsets.symmetric(
      horizontal: SpecHorizontal(40), vertical: SpecVertical(46))
  EdgeInsets get contentPadding;

  @SpecAbsoluteFontSize(24)
  double get loadingFontSize;

  @SpecHeight(36)
  double get dotSize;

  @SpecHeight(72)
  double get feedbackIconSize;

  @SpecHeight(20)
  double get dotBottomSpacing;

  static GuruFeedbackLoadingDesignSpec create(Size size,
          {Offset offset = Offset.zero}) =>
      _GuruFeedbackLoadingDesignSpec.from(size, offset: offset);
}

class GuruCancellableLoadingWidget extends StatefulWidget {
  final Stream<dynamic> errorStream;
  final Stream<bool> cancellableStream;
  final Completer completer;
  final Widget? loadingIcon;
  final String scene;

  const GuruCancellableLoadingWidget(
      this.cancellableStream, this.errorStream, this.completer,
      {Key? key, this.scene = "", this.loadingIcon})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return GuruCancellableLoadingState();
  }
}

class GuruCancellableLoadingState extends State<GuruCancellableLoadingWidget> {
  late GuruLoadingDesignSpec loadingDesignSpec;
  late GuruCancellableLoadingDesignSpec designSpec;

  @override
  void initState() {
    super.initState();
    loadingDesignSpec = GuruLoadingDesignSpec.get();
    designSpec = loadingDesignSpec.cancellableLoadingDesignSpec;
  }

  @override
  void dispose() {
    super.dispose();
  }

  Widget buildLoading(GuruThemeData guruTheme) {
    final appStrings = WidgetsStrings.get();
    final expressiveColor =
        guruTheme.colorScheme.overlayColors?.expressiveColor ??
            const Color(0xff33F8B4);
    final textColor =
        guruTheme.colorScheme.overlayColors?.textColor ?? Colors.white;
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        widget.loadingIcon ??
            ColorFiltered(
                colorFilter:
                    ColorFilter.mode(expressiveColor, BlendMode.modulate),
                child: LottieWidget(
                    uri: "assets/lottie/loading.json",
                    width: designSpec.loadingIconSize,
                    height: designSpec.loadingIconSize,
                    action: LottieAction.repeat,
                    package: "guru_widgets")),
        SizedSpacer(height: designSpec.loadingIconBottomSpacing),
        Text(
          appStrings.loading,
          textAlign: TextAlign.center,
          style: TextStyle(
              fontWeight: FontWeight.w500,
              color: textColor,
              fontSize: designSpec.loadingFontSize),
        ),
        const SizedSpacer(height: 4.0)
      ],
    );
  }

  Widget buildError(GuruThemeData guruTheme, String error) {
    final textColor =
        guruTheme.colorScheme.overlayColors?.textColor ?? Colors.white;
    return Center(
        child: AutoSizeText(error,
            maxLines: 3,
            textAlign: TextAlign.center,
            strutStyle: const StrutStyle(height: 1.4),
            style: TextStyle(
                fontWeight: FontWeight.w500,
                color: textColor,
                fontSize: designSpec.loadingFontSize)));
  }

  Widget buildContent(GuruThemeData guruTheme, bool cancelable, dynamic error) {
    final backgroundColor =
        guruTheme.colorScheme.overlayColors?.backgroundColor ??
            Colors.black.withOpacity(0.9);
    return FlexibleContainer(
        color: backgroundColor,
        radius:
            cancelable ? loadingDesignSpec.topRadius : loadingDesignSpec.radius,
        padding: error != null ? designSpec.contentPadding : const EdgeInsets.all(0.0),
        constraints: BoxConstraints(minHeight: designSpec.contentMinHeight, minWidth: designSpec.measuredSize.width),
        child: error != null
            ? buildError(guruTheme, error)
            : buildLoading(guruTheme));
  }

  Widget buildBottom(GuruThemeData guruTheme, bool cancelable) {
    final appStrings = WidgetsStrings.get();
    final dividerColor = guruTheme.colorScheme.overlayColors?.dividerColor ??
        const Color(0xff5B5B5B);
    final textColor =
        guruTheme.colorScheme.overlayColors?.textColor ?? Colors.white;
    final backgroundColor =
        guruTheme.colorScheme.overlayColors?.backgroundColor ??
            Colors.black.withOpacity(0.9);
    return AnimatedOpacity(
        opacity: cancelable ? 1.0 : 0.0,
        duration: const Duration(milliseconds: 150),
        child: FlexibleContainer(
          height: designSpec.cancelButtonHeight,
          color: cancelable ? backgroundColor : Colors.transparent,
          radius: loadingDesignSpec.bottomRadius,
          child: Stack(
            children: [
              Align(
                alignment: Alignment.topCenter,
                child: HorizontalDivider(
                    height: designSpec.dividerSize,
                    thickness: 0.5,
                    color: dividerColor),
              ),
              TapWidget(
                  onTap: () {
                    widget.completer?.complete();
                  },
                  inkWell: true,
                  materialBorderRadius: loadingDesignSpec.bottomRadius,
                  child: Center(
                    child: Text(
                      appStrings.cancel,
                      textAlign: TextAlign.center,
                      style: TextStyle(
                          color: textColor,
                          fontWeight: FontWeight.w500,
                          fontSize: designSpec.cancelFontSize),
                    ),
                  ))
            ],
          ),
        ));
  }

  @override
  Widget build(BuildContext context) {
    final guruTheme = GuruTheme.of(context);
    return StreamBuilder<Tuple2<bool, dynamic>>(
        stream: Rx.combineLatest2(widget.cancellableStream, widget.errorStream,
            (cancellable, error) {
          return Tuple2(cancellable, error);
        }),
        builder: (context, snapshot) {
          final cancelable = snapshot.data?.item1 == true;
          final error = snapshot.data?.item2;
          return SizedBox(
            width: designSpec.measuredSize.width,
            height: designSpec.measuredSize.height,
            child: Column(
              children: [
                buildContent(guruTheme, cancelable, error),
                buildBottom(guruTheme, cancelable)
              ],
            ),
          );
        });
  }
}

class GuruSimpleLoadingWidget extends StatefulWidget {
  final Widget? loadingIcon;
  final String scene;

  const GuruSimpleLoadingWidget({Key? key, this.loadingIcon, this.scene = ""})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return GuruSimpleLoadingState();
  }
}

class GuruSimpleLoadingState extends State<GuruSimpleLoadingWidget> {
  late GuruLoadingDesignSpec loadingDesignSpec;
  late GuruSimpleLoadingDesignSpec designSpec;

  @override
  void initState() {
    super.initState();
    loadingDesignSpec = GuruLoadingDesignSpec.get();
    designSpec = loadingDesignSpec.simpleLoadingDesignSpec;
  }

  @override
  void dispose() {
    super.dispose();
  }

  Widget buildLoading(GuruThemeData guruTheme) {
    final appStrings = WidgetsStrings.get();
    final expressiveColor =
        guruTheme.colorScheme.overlayColors?.expressiveColor ??
            const Color(0xff33F8B4);
    final textColor =
        guruTheme.colorScheme.overlayColors?.textColor ?? Colors.white;
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        widget.loadingIcon ??
            ColorFiltered(
                colorFilter:
                    ColorFilter.mode(expressiveColor, BlendMode.modulate),
                child: LottieWidget(
                    uri: "assets/lottie/loading.json",
                    width: designSpec.loadingIconSize,
                    height: designSpec.loadingIconSize,
                    action: LottieAction.repeat,
                    package: "guru_widgets")),
        SizedSpacer(height: designSpec.dotBottomSpacing),
        Text(
          appStrings.loading,
          textAlign: TextAlign.center,
          style: TextStyle(
              fontWeight: FontWeight.w500,
              color: textColor,
              fontSize: designSpec.loadingFontSize),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    final guruTheme = GuruTheme.of(context);
    final backgroundColor =
        guruTheme.colorScheme.overlayColors?.backgroundColor ??
            Colors.black.withOpacity(0.9);
    return FlexibleContainer(
        constraints: BoxConstraints(
            maxWidth: designSpec.measuredSize.width,
            minWidth: designSpec.measuredSize.width,
            minHeight: designSpec.measuredSize.height),
        padding: designSpec.contentPadding,
        color: backgroundColor,
        radius: loadingDesignSpec.radius,
        child: buildLoading(guruTheme));
  }
}

class GuruFeedbackLoadingWidget extends StatefulWidget {
  final Stream<bool> successStream;
  final Widget? loadingIcon;
  final Widget? feedbackIcon;
  final String? feedbackText;
  final String scene;

  const GuruFeedbackLoadingWidget(this.successStream,
      {Key? key,
      this.feedbackText,
      this.feedbackIcon,
      this.loadingIcon,
      this.scene = ""})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return GuruFeedbackLoadingState();
  }
}

class GuruFeedbackLoadingState extends State<GuruFeedbackLoadingWidget> {
  late GuruLoadingDesignSpec loadingDesignSpec;
  late GuruFeedbackLoadingDesignSpec designSpec;

  @override
  void initState() {
    super.initState();
    loadingDesignSpec = GuruLoadingDesignSpec.get();
    designSpec = loadingDesignSpec.feedbackLoadingDesignSpec;
  }

  @override
  void dispose() {
    super.dispose();
  }

  Widget buildIcon(GuruThemeData guruTheme) {
    final expressiveColor =
        guruTheme.colorScheme.overlayColors?.expressiveColor ??
            const Color(0xff33F8B4);
    return widget.feedbackIcon ??
        ColorFiltered(
            colorFilter: ColorFilter.mode(expressiveColor, BlendMode.modulate),
            child: LottieWidget(
                uri: "assets/lottie/successful.json",
                action: LottieAction.forward,
                width: designSpec.feedbackIconSize,
                height: designSpec.feedbackIconSize,
                package: "guru_widgets"));
  }

  Widget buildLoading(GuruThemeData guruTheme) {
    final expressiveColor =
        guruTheme.colorScheme.overlayColors?.expressiveColor ??
            const Color(0xff33F8B4);
    return widget.loadingIcon ??
        ColorFiltered(
            colorFilter: ColorFilter.mode(expressiveColor, BlendMode.modulate),
            child: LottieWidget(
                uri: "assets/lottie/loading.json",
                width: designSpec.feedbackIconSize,
                height: designSpec.feedbackIconSize,
                action: LottieAction.repeat,
                package: "guru_widgets"));
  }

  Widget buildContent(GuruThemeData guruTheme) {
    final appStrings = WidgetsStrings.get();
    final textColor =
        guruTheme.colorScheme.overlayColors?.textColor ?? Colors.white;
    return StreamBuilder(
        stream: widget.successStream,
        builder: ((context, snapshot) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              SizedBox(
                  width: designSpec.feedbackIconSize,
                  height: designSpec.feedbackIconSize,
                  child: snapshot.data == true
                      ? buildIcon(guruTheme)
                      : buildLoading(guruTheme)),
              SizedSpacer(height: designSpec.dotBottomSpacing),
              AutoSizeText(
                  snapshot.data == true
                      ? widget.feedbackText ?? ''
                      : appStrings.loading,
                  maxLines: 3,
                  textAlign: TextAlign.center,
                  style: TextStyle(
                      fontWeight: FontWeight.w500,
                      color: textColor,
                      fontSize: designSpec.loadingFontSize))
            ],
          );
        }));
  }

  @override
  Widget build(BuildContext context) {
    final guruTheme = GuruTheme.of(context);
    final backgroundColor =
        guruTheme.colorScheme.overlayColors?.backgroundColor ??
            Colors.black.withOpacity(0.9);

    return FlexibleContainer(
        constraints: BoxConstraints(
            maxWidth: designSpec.measuredSize.width,
            minWidth: designSpec.measuredSize.width,
            minHeight: designSpec.measuredSize.height),
        padding: designSpec.contentPadding,
        radius: loadingDesignSpec.radius,
        color: backgroundColor,
        child: buildContent(guruTheme));
  }
}

class GuruStandardOverlayWidget extends StatefulWidget {
  final String? icon;
  final String text;
  final String scene;
  final String? package;

  const GuruStandardOverlayWidget({Key? key, this.icon, this.text = "", this.scene = "", this.package})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return GuruStandardOverlayState();
  }
}

class GuruStandardOverlayState extends State<GuruStandardOverlayWidget> {
  late GuruLoadingDesignSpec loadingDesignSpec;
  late GuruFeedbackLoadingDesignSpec designSpec;

  @override
  void initState() {
    super.initState();
    loadingDesignSpec = GuruLoadingDesignSpec.get();
    designSpec = loadingDesignSpec.feedbackLoadingDesignSpec;
  }

  @override
  void dispose() {
    super.dispose();
  }

  Widget buildContent(GuruThemeData guruTheme) {
    final appStrings = WidgetsStrings.get();
    final textColor =
        guruTheme.colorScheme.overlayColors?.textColor ?? Colors.white;
    

    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        SizedBox(
            width: designSpec.feedbackIconSize,
            height: designSpec.feedbackIconSize,
            child: widget.icon != null ? Image.asset(widget.icon!,
                            width: designSpec.feedbackIconSize,
                            height: designSpec.feedbackIconSize,
                            package: widget.package,) : Icon(Icons.fork_right, size: designSpec.feedbackIconSize, color: textColor)),
        SizedSpacer(height: designSpec.dotBottomSpacing),
        AutoSizeText(widget.text,
            maxLines: 3,
            textAlign: TextAlign.center,
            style: TextStyle(
                fontWeight: FontWeight.w500,
                color: textColor,
                fontSize: designSpec.loadingFontSize))
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    final guruTheme = GuruTheme.of(context);
    final backgroundColor =
        guruTheme.colorScheme.overlayColors?.backgroundColor ??
            Colors.black.withOpacity(0.9);

    return FlexibleContainer(
        constraints: BoxConstraints(
            maxWidth: designSpec.measuredSize.width,
            minWidth: designSpec.measuredSize.width,
            minHeight: designSpec.measuredSize.height),
        padding: designSpec.contentPadding,
        radius: loadingDesignSpec.radius,
        color: backgroundColor,
        child: buildContent(guruTheme));
  }
}

class GuruToastAnimatedWidget extends StatefulWidget {
  final Widget child; 

  const GuruToastAnimatedWidget(
      {Key? key,
      required this.child})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return GuruToastAnimatedState();
  }
}

class GuruToastAnimatedState extends State<GuruToastAnimatedWidget> with SingleTickerProviderStateMixin {
  late final AnimationController _controller = AnimationController(
    duration: const Duration(milliseconds: 1000),
    vsync: this,
  )..forward();

  /// 0 为完全透明， 1为完全不透明
  late final Animation<double> _animation = Tween<double>(
    begin: 0,
    end: 1,
  ).animate(_controller);

  @override
  void dispose() {
    _controller.reverse();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: widget.child,
    );
  }
}