import 'dart:collection';

import 'package:design/design.dart';
import 'package:flutter/material.dart';
import 'package:guru_widgets/common/spacer.dart';
import 'package:guru_widgets/guru_widgets.dart';
import 'package:guru_widgets/image/adaptive_image.dart';
import 'package:guru_widgets/theme/guru_theme.dart';
import 'package:guru_utils/widget/widget_utils.dart';
import 'package:system_clock/system_clock.dart';
import 'package:guru_utils/feedback/feedback_manager.dart';
import 'package:guru_utils/tuple/tuple.dart';

/// Created by Haoyi on 2023/5/20

part 'guru_button.g.dart';

@DesignSpec(width: 750, height: 1624, specMode: SpecMode.useGet)
abstract class GuruButtonStyleDesignSpec implements BasicDesignSpec {
  @SpecHeight(104)
  double get s1;

  @SpecHeight(96)
  double get s2;

  @SpecHeight(80)
  double get s3;

  @SpecHeight(72)
  double get s4;

  @SpecHeight(60)
  double get s5;

  static GuruButtonStyleDesignSpec get() {
    return _GuruButtonStyleDesignSpec.get();
  }
}

enum GuruButtonSizeSpec { s1, s2, s3, s4, s5 }

abstract class GuruButtonDesignSpec implements BasicDesignSpec {
  double get itemHorizontalSpacing;

  double get infoMinWidth;

  double get iconSize;

  double get mainFontSize;

  double get summaryFontSize;

  static GuruButtonDesignSpec from(GuruButtonSizeSpec sizeSpec, Size size) {
    switch (sizeSpec) {
      case GuruButtonSizeSpec.s1:
        return GuruButtonS1DesignSpec.from(size);
      case GuruButtonSizeSpec.s2:
        return GuruButtonS2DesignSpec.from(size);
      case GuruButtonSizeSpec.s3:
        return GuruButtonS3DesignSpec.from(size);
      case GuruButtonSizeSpec.s4:
        return GuruButtonS4DesignSpec.from(size);
      case GuruButtonSizeSpec.s5:
        return GuruButtonS5DesignSpec.from(size);
    }
  }
}

@DesignSpec(width: 630, height: 104, specMode: SpecMode.useSize)
abstract class GuruButtonS1DesignSpec implements GuruButtonDesignSpec {
  @override
  @SpecHorizontal(12)
  double get itemHorizontalSpacing;

  @override
  @SpecHorizontal(72)
  double get infoMinWidth;

  @override
  @SpecHeight(56)
  double get iconSize;

  @override
  @SpecAbsoluteFontSize(36, consistent: true)
  double get mainFontSize;

  @override
  @SpecAbsoluteFontSize(20)
  double get summaryFontSize;

  static GuruButtonS1DesignSpec from(Size size, {Offset offset = Offset.zero}) =>
      _GuruButtonS1DesignSpec.from(size, offset: offset);
}

@DesignSpec(width: 542, height: 96, specMode: SpecMode.useSize)
abstract class GuruButtonS2DesignSpec implements GuruButtonDesignSpec {
  @override
  @SpecHorizontal(12)
  double get itemHorizontalSpacing;

  @override
  @SpecHorizontal(72)
  double get infoMinWidth;

  @override
  @SpecHeight(56)
  double get iconSize;

  @override
  @SpecAbsoluteFontSize(32, consistent: true)
  double get mainFontSize;

  @override
  @SpecAbsoluteFontSize(20, consistent: true)
  double get summaryFontSize;

  static GuruButtonS2DesignSpec from(Size size, {Offset offset = Offset.zero}) =>
      _GuruButtonS2DesignSpec.from(size, offset: offset);
}

@DesignSpec(width: 480, height: 80, specMode: SpecMode.useSize)
abstract class GuruButtonS3DesignSpec implements GuruButtonDesignSpec {
  @override
  @SpecHorizontal(12)
  double get itemHorizontalSpacing;

  @override
  @SpecHorizontal(72)
  double get infoMinWidth;

  @override
  @SpecHeight(48)
  double get iconSize;

  @override
  @SpecAbsoluteFontSize(28, consistent: true)
  double get mainFontSize;

  @override
  @SpecAbsoluteFontSize(18, consistent: true)
  double get summaryFontSize;

  static GuruButtonS3DesignSpec from(Size size, {Offset offset = Offset.zero}) =>
      _GuruButtonS3DesignSpec.from(size, offset: offset);
}

@DesignSpec(width: 400, height: 72, specMode: SpecMode.useSize)
abstract class GuruButtonS4DesignSpec implements GuruButtonDesignSpec {
  @override
  @SpecHorizontal(12)
  double get itemHorizontalSpacing;

  @override
  @SpecHorizontal(72)
  double get infoMinWidth;

  @override
  @SpecHeight(48)
  double get iconSize;

  @override
  @SpecAbsoluteFontSize(26, consistent: true)
  double get mainFontSize;

  @override
  @SpecAbsoluteFontSize(18, consistent: true)
  double get summaryFontSize;

  static GuruButtonS4DesignSpec from(Size size, {Offset offset = Offset.zero}) =>
      _GuruButtonS4DesignSpec.from(size, offset: offset);
}

@DesignSpec(width: 172, height: 60, specMode: SpecMode.useSize)
abstract class GuruButtonS5DesignSpec implements GuruButtonDesignSpec {
  @override
  @SpecHorizontal(12)
  double get itemHorizontalSpacing;

  @override
  @SpecHorizontal(72)
  double get infoMinWidth;

  @override
  @SpecHeight(44)
  double get iconSize;

  @override
  @SpecAbsoluteFontSize(26, consistent: true)
  double get mainFontSize;

  @override
  @SpecAbsoluteFontSize(18, consistent: true)
  double get summaryFontSize;

  static GuruButtonS5DesignSpec from(Size size, {Offset offset = Offset.zero}) =>
      _GuruButtonS5DesignSpec.from(size, offset: offset);
}

enum GuruButtonFillType { solid, outline }

class GuruButton extends StatefulWidget {
  final Size size;

  final VoidCallback? onPressed;
  final int throttleTime;

  final GuruButtonStyle? style;

  final GuruButtonFillType? fillType;

  final GuruButtonSizeSpec sizeSpec;

  final Widget? child;

  // Button 动作
  final String? action;

  // 前导图标
  final String? leading;

  // 在outline模式下，是否对前导图标进行着色
  final bool tintLeading;

  // 尾随图标
  final String? trailing;

  // 在outline模式下，是否对尾随图标进行着色
  final bool tintTrailing;

  // Button 信息（如金币数量，道具数量）
  final String? info;

  // Button 说明（第二行）
  final String? summary;

  // outline模式下，border宽度
  final double? borderWidth;

  const GuruButton({super.key,
    required this.size,
    this.child,
    this.onPressed,
    this.action,
    this.leading,
    this.tintLeading = false,
    this.trailing,
    this.tintTrailing = false,
    this.info,
    this.summary,
    this.throttleTime = 800,
    this.style,
    this.fillType,
    this.borderWidth,
    this.sizeSpec = GuruButtonSizeSpec.s2});

  @override
  State<StatefulWidget> createState() => GuruButtonState();
}

class GuruButtonState extends State<GuruButton> {
  bool pressed = false;
  int _latestTapTimestamp = 0;

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

  void _dispatchOnPress({FeedbackCapabilities capabilities = FeedbackCapabilities.sound}) {
    final elapsedMs = SystemClock
        .elapsedRealtime()
        .inMilliseconds;
    if (elapsedMs - _latestTapTimestamp > widget.throttleTime) {
      if (capabilities.canPerform()) {
        FeedbackManager.instance.perform(FeedbackOccasion.clickButton);
      }
      widget.onPressed?.call();
      _latestTapTimestamp = elapsedMs;
    }
  }

  Widget _buildContent(ThemeData themeData, GuruButtonDesignSpec designSpec,
      {bool isOutline = false, Color tintColor = Colors.white}) {
    final child = widget.child;
    if (child != null) {
      return child;
    }

    final items = <Widget>[];
    final leading = widget.leading;
    if (leading != null) {
      items.add(AdaptiveImage(leading,
          width: designSpec.iconSize,
          height: designSpec.iconSize,
          color: (isOutline && widget.tintLeading) ? tintColor : null));
    }
    final action = widget.action;
    if (action != null) {
      items.add(AutoSizeText(action,
          maxLines: 1,
          minFontSize: 10,
          style: TextStyle(
              color: tintColor,
              fontSize: designSpec.mainFontSize,
              fontWeight: GuruTheme.fwSemiBold)));
    }
    final trailing = widget.trailing;
    if (trailing != null) {
      items.add(AdaptiveImage(trailing,
          width: designSpec.iconSize,
          height: designSpec.iconSize,
          color: (isOutline && widget.tintTrailing) ? tintColor : null));
    }
    final info = widget.info;
    if (info != null) {
      items.add(ConstrainedBox(constraints: BoxConstraints(minWidth: designSpec.infoMinWidth), child: Text(info,
          style: TextStyle(
              fontSize: designSpec.mainFontSize,
              color: tintColor,
              fontWeight: GuruTheme.fwSemiBold))));
    }

    final columnItems = <Widget>[];
    if (items.length > 1) {
      final spacing = SizedSpacer(width: designSpec.itemHorizontalSpacing);
      columnItems.add(Center(
        child: Row(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            children: WidgetUtils.separate(items, spacing)),
      ));
    } else if (items.length == 1) {
      columnItems.add(Center(child: items.first));
    }

    final summary = widget.summary;
    if (summary != null) {
      columnItems.add(Center(
          child: Text(summary,
              style: TextStyle(
                  fontSize: designSpec.summaryFontSize,
                  color: tintColor,
                  fontWeight: GuruTheme.fwMedium))));
    }

    if (columnItems.length > 1) {
      return Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: columnItems);
    } else if (columnItems.length == 1) {
      return columnItems.first;
    } else {
      return const EmptySpacer();
    }
  }

  Widget buildDecorator(ThemeData themeData, GuruThemeData guruTheme, GuruButtonDecorator decorator,
      {Widget? child}) {
    late Widget wrapper;
    if (pressed && decorator.pressed != null) {
      wrapper = decorator.pressed!.call(child);
    } else {
      wrapper = decorator.active(child);
    }

    return GestureDetector(
        onTapDown: (_) {
          setState(() {
            pressed = true;
          });
        },
        onTapUp: (_) {
          setState(() {
            pressed = false;
          });
        },
        onTapCancel: () {
          setState(() {
            pressed = false;
          });
        },
        onTap: () {
          if (pressed) {
            setState(() {
              pressed = false;
            });
          }

          _dispatchOnPress(capabilities: guruTheme.feedbackCapabilities);
        },
        child: SizedBox(width: widget.size.width, height: widget.size.height, child: wrapper));
  }

  Widget buildElevatedButton(ThemeData themeData, GuruButtonDesignSpec designSpec, GuruThemeData guruTheme,
      GuruButtonElevatedScheme? elevatedScheme,
      {bool isOutline = false, Widget? child}) {
    final primaryColor = elevatedScheme?.backgroundColor ?? themeData.primaryColor;
    return SizedBox(
      width: widget.size.width, 
      height: widget.size.height,
      child: ElevatedButton(
      onPressed: () {
        _dispatchOnPress(capabilities: guruTheme.feedbackCapabilities);
      },
      style: ButtonStyle(
        shape: MaterialStateProperty.all<RoundedRectangleBorder>(
          RoundedRectangleBorder(
            side: isOutline ? BorderSide(color: primaryColor, width: widget.borderWidth ?? 2) : BorderSide.none,
            borderRadius:
            elevatedScheme?.radius ?? BorderRadius.circular(widget.size.height * 0.5), // 设置圆角半径
          ),
        ),
        backgroundColor:
        MaterialStateProperty.all<Color>(isOutline ? Colors.transparent : primaryColor),
        fixedSize: MaterialStateProperty.all<Size>(widget.size),
        elevation: MaterialStateProperty.all<double>(elevatedScheme?.elevation ?? 0),
        overlayColor: elevatedScheme?.overlayColor != null
            ? MaterialStateProperty.all<Color>(elevatedScheme!.overlayColor!)
            : isOutline
            ? MaterialStateProperty.all<Color>(primaryColor.withOpacity(0.2))
            : null,
        shadowColor: elevatedScheme?.shadowColor != null
            ? MaterialStateProperty.all<Color>(elevatedScheme!.shadowColor!)
            : null,
        minimumSize: MaterialStateProperty.all<Size>(Size(widget.size.width, designSpec.measuredSize.height)),
        maximumSize: MaterialStateProperty.all<Size>(Size(widget.size.width, designSpec.measuredSize.height))
      ),
      child: child,
    ));
  }

  @override
  Widget build(BuildContext context) {
    final themeData = Theme.of(context);
    final guruThemeData = GuruTheme.of(context);
    final designSpec = GuruButtonDesignSpec.from(widget.sizeSpec, widget.size);

    GuruButtonDecorator? decorator = guruThemeData.buttonTheme.defaultDecorator;
    final style = widget.style;
    if (style != null) {
      final styleDecorator = guruThemeData.buttonTheme.decorators[style];
      if (styleDecorator != null) {
        decorator = styleDecorator;
      } else if (guruThemeData.buttonTheme.elevatedSchemes[style] != null) {
        decorator = null;
      }
    }

    if (decorator != null) {
      final contentTintColor = decorator.contentTintColor ?? themeData.primaryColor;
      final child = _buildContent(themeData, designSpec, tintColor: contentTintColor);
      return buildDecorator(themeData, guruThemeData, decorator, child: child);
    } else {
      final elevatedScheme = style != null
          ? guruThemeData.buttonTheme.elevatedSchemes[style]
          : guruThemeData.buttonTheme.primaryElevatedScheme;
      final fillType = widget.fillType ?? elevatedScheme?.fillType ?? GuruButtonFillType.solid;
      final isOutline = fillType == GuruButtonFillType.outline;
      final child = _buildContent(themeData, designSpec,
          isOutline: isOutline,
          tintColor: (isOutline ? themeData.primaryColor : elevatedScheme?.contentTintColor) ??
              Colors.white);
      return buildElevatedButton(themeData, designSpec, guruThemeData, elevatedScheme,
          isOutline: isOutline, child: child);
    }
  }
}
