import 'dart:math';

import 'package:design/design.dart';
import 'package:guru_widgets/guru_widgets.dart';
import 'package:guru_widgets/theme/guru_theme.dart';
import 'package:guru_utils/random/random_utils.dart';
import 'package:guru_utils/number/range.dart';

part 'guru_slider_bar.g.dart';

@DesignSpec(width: 750, height: 120, specMode: SpecMode.nested)
abstract class GuruSliderBarDesignSpec implements BasicDesignSpec {
  @SpecSize(SpecWidth(562), SpecHeight(120))
  Size get sliderBarSize;

  @SpecHeight(38)
  double get barBottomInset;

  @SpecHeight(41)
  double get radius;

  @SpecVertical(4)
  double get padding;

  @SpecHeight(4)
  double get strokeWidth;

  @SpecSize(SpecHeight(72), SpecHeight(56))
  Size get anchorIconSize;

  @SpecHeight(60)
  double get multipleImageSize;

  @SpecHeight(60)
  double get multipleTextSize;

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

class GuruSliderBarItem {
  final int flex;
  final int data;
  final Decoration? background;
  final String multipleText;
  final String? multipleImage;

  const GuruSliderBarItem(
      {this.flex = 2,
      required this.data,
      this.background,
      this.multipleText = 'x2',
      this.multipleImage});
}

class GuruSliderBarController extends ChangeNotifier {
  List<Range<int>> ranges = [];
  bool isAnimating;
  int data = 0;
  double position = 0;

  void initRanges (List<GuruSliderBarItem> items, GuruSliderBarDesignSpec designSpec) {
    int flexCount = 0;
    for (var item in items) {
      flexCount += item.flex;
    }
    double start = 0;
    double end = 0;
    for (var i = 0; i < items.length; i++) {
      final item = items[i];
      end += (item.flex * (designSpec.sliderBarSize.width / flexCount));
      if (i == 0) {
        end -= designSpec.anchorIconSize.width / 2;
      }
      ranges.add(Range(start, end, data: item.data));
      start = end;
    }
  }

  void updatePositon(double pos) {
    position = pos;
    updateMultiple(pos);
  }

  void start() {
    if (!isAnimating) {
      isAnimating = true;
      notifyListeners();
    }
  }

  void stop() {
    if (isAnimating) {
      isAnimating = false;
      notifyListeners();
    }
  }

  void updateMultiple(double pos) {
    for (var range in ranges) {
      if (range.inRange(pos)) {
        if (data != range.data) {
          data = range.data!;
          notifyListeners();
        }
      }
    }
  }

  @override
  void dispose() {
    isAnimating = true;
    data = 0;
    ranges = [];
    position = 0;
    super.dispose();
  }

  GuruSliderBarController({this.isAnimating = true});
}

class GuruSliderBar extends StatefulWidget {
  final GuruSliderBarController controller;
  final List<GuruSliderBarItem>? items;
  final String? anchorIcon;
  final double? anchorOffset;
  final Decoration? background;
  final double? horizontalOffset;

  const GuruSliderBar(
      {super.key,
      required this.controller,
      this.items,
      this.anchorIcon,
      this.anchorOffset,
      this.background,
      this.horizontalOffset});

  @override
  State<GuruSliderBar> createState() => _GuruSliderBarState();
}

class _GuruSliderBarState extends State<GuruSliderBar>
    with TickerProviderStateMixin {
  late AnimationController animationController;
  late Animation<double>? tween;
  final designSpec = GuruThemeDesignSpec.get().sliderBarDesignSpec;
  static List<Color> colors = [
    const Color(0xFF73AC35),
    const Color(0xFFF98B1B),
    const Color(0xFFFFCC00),
    const Color(0xFFDD192A)
  ];
  final defaultItems = [
    const GuruSliderBarItem(data: 2, multipleText: 'x2'),
    const GuruSliderBarItem(data: 3, multipleText: 'x3'),
    const GuruSliderBarItem(data: 4, multipleText: 'x4'),
    const GuruSliderBarItem(data: 3, multipleText: 'x3'),
    const GuruSliderBarItem(data: 2, multipleText: 'x2'),
  ];
  late List<GuruSliderBarItem> items;

  @override
  void initState() {
    super.initState();
    final guruTheme = GuruTheme.of(Get.context!);
    final sliderBarTheme = guruTheme.sliderBarTheme;
    final duration = sliderBarTheme.duration ?? const Duration(milliseconds: 1400);

    items = widget.items ?? sliderBarTheme.items ?? defaultItems;
    widget.controller.initRanges(items, designSpec);
    
    widget.controller.addListener(() {
      if (animationController.isAnimating && !widget.controller.isAnimating) {
        animationController.stop(canceled: false);
      }
      if (!animationController.isAnimating && widget.controller.isAnimating) {
        animationController.repeat( min: 0.0, max: 1.0, reverse: true, period: duration);
      }
    });

    animationController = AnimationController(vsync: this);

    final curved = CurvedAnimation(parent: animationController, curve: sliderBarTheme.curve ?? Curves.linear);
    final double tweenBegin = widget.anchorOffset ?? sliderBarTheme.anchorOffset ?? 0;
    tween = Tween(begin: tweenBegin, end: designSpec.sliderBarSize.width - designSpec.anchorIconSize.width - (2 * tweenBegin)).animate(curved);
    if (widget.controller.isAnimating) {
      animationController.repeat(min: 0.0, max: 1.0, reverse: true, period: duration);
    }
  }

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

  Widget _buildItem(GuruSliderBarItem item) {
    Widget text = item.multipleImage != null
        ? Image.asset(
            item.multipleImage!,
            width: designSpec.multipleImageSize,
            height: designSpec.multipleImageSize,
          )
        : Text(item.multipleText,
            style: TextStyle(
                fontSize: designSpec.multipleTextSize, color: Colors.white));

    return Expanded(
      flex: item.flex,
      child: DecoratedBox(
        decoration: item.background ??
            BoxDecoration(color: colors[RandomUtils.nextInt(4)]),
        child: Center(child: text),
      ),
    );
  }

  Widget _buidlAnchor() {
    final guruTheme = GuruTheme.of(context);
    final icon = widget.anchorIcon ?? guruTheme.sliderBarTheme.anchorIcon;

    final anchor = icon != null
        ? Image.asset(icon,
            width: designSpec.anchorIconSize.width,
            height: designSpec.anchorIconSize.height)
        : Icon(Icons.stars,
            size: designSpec.anchorIconSize.width, color: Colors.orangeAccent);

    return Align(
        alignment: Alignment.bottomLeft,
        child: AnimatedBuilder(
            animation: animationController,
            child: anchor,
            builder: (context, child) {
              final pos = tween!.value;
              widget.controller.updatePositon(pos);
              return Transform.translate(
                  offset: Offset(pos, 0), child: child);
            }));
  }

  @override
  Widget build(BuildContext context) {
    final guruTheme = GuruTheme.of(context);
    final sliderTheme = guruTheme.sliderBarTheme;
    final background = widget.background ??
        sliderTheme.background ??
        BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(designSpec.radius)),
            border:
                Border.all(color: Colors.black.withOpacity(0.1), width: designSpec.strokeWidth));

    return Container(
        alignment: Alignment.center,
        child: SizedBox(
            width: designSpec.sliderBarSize.width,
            height: designSpec.sliderBarSize.height,
            child: Stack(children: [
              Positioned.fill(
                  bottom: designSpec.barBottomInset,
                  child: DecoratedBox(
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.all(
                            Radius.circular(designSpec.radius)),
                        color: Colors.white),
                    child: Padding(
                        padding: EdgeInsets.all(designSpec.padding),
                        child: ClipRRect(
                          borderRadius: BorderRadius.all(
                              Radius.circular(designSpec.radius)),
                          child: Stack(
                            fit: StackFit.expand,
                            children: [
                              Row(
                                mainAxisSize: MainAxisSize.max,
                                children: [
                                  for (var item in items) _buildItem(item)
                                ],
                              ),
                              DecoratedBox(decoration: background),
                            ],
                          ),
                        )),
                  )),
              _buidlAnchor()
            ])));
  }
}
