import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:kasy_kit/components/kasy_button.dart';
import 'package:kasy_kit/core/theme/theme.dart';

/// Tint for [KasyDialog.leadingIcon] bubble (aligned with sheet / alert semantics).
enum KasyDialogIconTone { info, success, warning, danger, neutral }

/// Visual treatment for the optional trailing dismiss control on [KasyDialog].
enum KasyDialogCloseButtonStyle {
  /// Compact Material icon button (legacy).
  standard,

  /// Circular neutral fill via [KasyButton.iconOnly] (design-system close chip).
  filledNeutral,
}

/// Centered modal card: icon bubble, title, body, optional dismiss control, actions.
///
/// **Single trailing button:** pass [confirmLabel] (and optional [onConfirm]).
///
/// **Stacked full-width buttons:** pass [actions]. When non-empty, it replaces
/// [confirmLabel]. If both are set, [actions] wins. Use [actionsAxis] ==
/// [Axis.horizontal] for a proportional side-by-side row instead of the
/// default column.
///
/// **[footer]** replaces that default footer entirely (custom rows, dual actions).
///
/// **Custom body:** when [body] is non-null it is rendered after the title; otherwise
/// a non-null, non-empty [message] renders the dimmed subtitle text.
///
/// Present with [showKasyDialog] or [showKasyBlurDialog]; prefer [KasyButton] in [actions].
///
/// Omit [leadingIcon] (leave null) for a compact header row: title and close button only.
/// Use [titleCentered] together with null [leadingIcon] to mirror centered header layouts.
/// Use [closeAboveTitle] with null [leadingIcon] and [titleCentered] false to put the close
/// control on its own top row (trailing), with the title full-width on the line below.
/// Use [closeButtonStyle] to swap the trailing dismiss affordance (e.g. neutral filled circle).
/// For [KasyDialogCloseButtonStyle.filledNeutral], optional [closeFilledButtonExtent] and
/// [closeFilledIconGlyphSize] tune chip size and glyph without changing defaults elsewhere.
class KasyDialog extends StatelessWidget {
  final IconData? leadingIcon;
  final KasyDialogIconTone iconTone;
  final String title;
  /// When [leadingIcon] is null and this is true, the title aligns to the centered column.
  final bool titleCentered;
  /// When true (and [leadingIcon] is null, [titleCentered] is false, [showCloseButton] is true),
  /// close sits on a dedicated top row (end-aligned), title on the next row below.
  final bool closeAboveTitle;
  /// Subtitle paragraph; omitted when null/empty unless [body] is used ([body] wins).
  final String? message;
  /// Replaces the default subtitle when set (e.g. form fields).
  final Widget? body;
  /// Replaces computed confirm / [actions] footer when non-null.
  final Widget? footer;

  /// Used when [footer] is null and [actions] is null or empty (basic dialog).
  final String? confirmLabel;
  final VoidCallback? onConfirm;

  /// When non-empty, replaces the single trailing [confirmLabel] row (full-width column).
  final List<Widget>? actions;

  /// When [actions] is non-empty, lays them out horizontally with equal flex
  /// ([Expanded]) instead of a full-width column.
  final Axis actionsAxis;

  final VoidCallback? onClose;
  final bool showCloseButton;

  /// How the trailing close control is rendered when [showCloseButton] is true.
  final KasyDialogCloseButtonStyle closeButtonStyle;

  /// Overrides the circular hit area for [KasyDialogCloseButtonStyle.filledNeutral] (icon-only extent).
  final double? closeFilledButtonExtent;

  /// Overrides the close icon glyph size for [KasyDialogCloseButtonStyle.filledNeutral].
  final double? closeFilledIconGlyphSize;

  const KasyDialog({
    super.key,
    this.leadingIcon,
    this.iconTone = KasyDialogIconTone.warning,
    required this.title,
    this.titleCentered = false,
    this.closeAboveTitle = false,
    this.message,
    this.body,
    this.footer,
    this.confirmLabel,
    this.onConfirm,
    this.actions,
    this.actionsAxis = Axis.vertical,
    this.onClose,
    this.showCloseButton = true,
    this.closeButtonStyle = KasyDialogCloseButtonStyle.standard,
    this.closeFilledButtonExtent,
    this.closeFilledIconGlyphSize,
  });

  static const double _maxCardWidth = 400;

  bool get _usesActionList => actions != null && actions!.isNotEmpty;

  @override
  Widget build(BuildContext context) {
    if (footer == null) {
      assert(
        _usesActionList ||
            (confirmLabel != null && confirmLabel!.trim().isNotEmpty),
      );
    }

    void handleClose() {
      if (onClose != null) {
        onClose!();
      } else {
        Navigator.of(context).maybePop();
      }
    }

    Widget buildCloseTrailing() {
      switch (closeButtonStyle) {
        case KasyDialogCloseButtonStyle.filledNeutral:
          return KasyButton.iconOnly(
            icon: KasyIcons.close,
            variant: KasyButtonVariant.neutral,
            size: KasyButtonSize.small,
            iconOnlyLayoutExtent: closeFilledButtonExtent,
            iconGlyphSize: closeFilledIconGlyphSize,
            semanticLabel: MaterialLocalizations.of(context).closeButtonTooltip,
            onPressed: handleClose,
          );
        case KasyDialogCloseButtonStyle.standard:
          return Transform.translate(
            offset: const Offset(0, -4),
            child: IconButton(
              onPressed: handleClose,
              visualDensity: VisualDensity.compact,
              style: IconButton.styleFrom(
                foregroundColor: context.colors.muted,
                tapTargetSize: MaterialTapTargetSize.shrinkWrap,
                padding: const EdgeInsets.all(6),
                minimumSize: const Size(40, 40),
              ),
              icon: const Icon(KasyIcons.close, size: 19),
            ),
          );
      }
    }

    final TextStyle titleStyle =
        context.textTheme.titleLarge?.copyWith(
          fontWeight: FontWeight.w600,
          color: context.colors.onSurface,
        ) ??
        TextStyle(fontWeight: FontWeight.w600, fontSize: 20, color: context.colors.onSurface);

    late final Widget headerBlock;
    if (leadingIcon != null) {
      headerBlock = Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _DialogIconBubble(icon: leadingIcon!, tone: iconTone),
              const Spacer(),
              if (showCloseButton) buildCloseTrailing(),
            ],
          ),
          const SizedBox(height: KasySpacing.sm),
          Text(title, style: titleStyle),
        ],
      );
    } else if (closeAboveTitle && showCloseButton) {
      headerBlock = Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [buildCloseTrailing()],
          ),
          const SizedBox(height: KasySpacing.sm),
          Text(
            title,
            style: titleStyle,
            textAlign: titleCentered ? TextAlign.center : TextAlign.start,
          ),
        ],
      );
    } else if (titleCentered) {
      headerBlock = Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const SizedBox(width: 40),
          Expanded(
            child: Text(title, textAlign: TextAlign.center, style: titleStyle),
          ),
          if (showCloseButton)
            SizedBox(width: 40, child: buildCloseTrailing())
          else
            const SizedBox(width: 40),
        ],
      );
    } else {
      headerBlock = Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(child: Text(title, style: titleStyle)),
          if (showCloseButton) buildCloseTrailing(),
        ],
      );
    }

    final Widget footerWidget =
        footer ??
        (_usesActionList
            ? actionsAxis == Axis.horizontal
                  ? Row(
                      children: [
                        for (int i = 0; i < actions!.length; i++) ...[
                          if (i > 0) const SizedBox(width: KasySpacing.sm),
                          Expanded(child: actions![i]),
                        ],
                      ],
                    )
                  : Column(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      children: [
                        for (int i = 0; i < actions!.length; i++) ...[
                          if (i > 0) const SizedBox(height: KasySpacing.sm),
                          actions![i],
                        ],
                      ],
                    )
            : Row(
                children: [
                  const Spacer(),
                  KasyButton(
                    label: confirmLabel!,
                    variant: KasyButtonVariant.neutral,
                    onPressed: onConfirm,
                  ),
                ],
              ));

    return ConstrainedBox(
      constraints: const BoxConstraints(maxWidth: _maxCardWidth),
      child: DecoratedBox(
        decoration: BoxDecoration(
          color: context.colors.surface,
          borderRadius: BorderRadius.circular(KasyRadius.lg),
          border: Border.all(
            color: context.colors.outline.withValues(alpha: 0.22),
          ),
          boxShadow: [
            KasyShadows.component(
              context,
              blurRadius: 20,
              spreadRadius: -2,
              offset: const Offset(0, 8),
            ),
          ],
        ),
        child: Padding(
          padding: const EdgeInsets.all(KasySpacing.md),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              headerBlock,
              if (body != null) ...[
                SizedBox(height: leadingIcon != null ? KasySpacing.sm : KasySpacing.md),
                body!,
              ] else if (message != null && message!.trim().isNotEmpty) ...[
                const SizedBox(height: KasySpacing.sm),
                Text(
                  message!,
                  textAlign: titleCentered ? TextAlign.center : TextAlign.start,
                  style: context.textTheme.bodyMedium?.copyWith(
                    fontSize: 15,
                    color: context.colors.onSurface.withValues(alpha: 0.6),
                    height: 1.5,
                  ),
                ),
              ],
              const SizedBox(height: KasySpacing.lg),
              footerWidget,
            ],
          ),
        ),
      ),
    );
  }
}

/// Scrollable region with a max height and optional top/bottom fades into the dialog surface tone.
class KasyDialogScrollableBody extends StatefulWidget {
  static const double _fadeHeight = 28;

  final double maxHeight;
  final Widget child;
  final bool fadeBottomEdge;
  final bool fadeTopEdge;
  final EdgeInsetsGeometry padding;

  const KasyDialogScrollableBody({
    super.key,
    required this.maxHeight,
    required this.child,
    this.fadeBottomEdge = true,
    this.fadeTopEdge = true,
    this.padding = EdgeInsets.zero,
  });

  @override
  State<KasyDialogScrollableBody> createState() =>
      _KasyDialogScrollableBodyState();
}

class _KasyDialogScrollableBodyState extends State<KasyDialogScrollableBody> {
  final ScrollController _scrollController = ScrollController();
  bool _showTopFade = false;
  bool _showBottomFade = true;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
  }

  @override
  void dispose() {
    _scrollController.removeListener(_onScroll);
    _scrollController.dispose();
    super.dispose();
  }

  void _onScroll() {
    final bool atTop = _scrollController.offset <= 0;
    final bool atBottom = _scrollController.offset >=
        _scrollController.position.maxScrollExtent;
    final bool newShowTop = !atTop;
    final bool newShowBottom = !atBottom;
    if (newShowTop != _showTopFade || newShowBottom != _showBottomFade) {
      setState(() {
        _showTopFade = newShowTop;
        _showBottomFade = newShowBottom;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final Color surfaceColor = context.colors.surface;
    final Widget scrollable = Padding(
      padding: widget.padding,
      child: SingleChildScrollView(
        controller: _scrollController,
        child: widget.child,
      ),
    );
    if (!widget.fadeBottomEdge && !widget.fadeTopEdge) {
      return ConstrainedBox(
        constraints: BoxConstraints(maxHeight: widget.maxHeight),
        child: scrollable,
      );
    }
    return ConstrainedBox(
      constraints: BoxConstraints(maxHeight: widget.maxHeight),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(KasyRadius.md),
        child: Stack(
          children: [
            Positioned.fill(child: scrollable),
            if (widget.fadeTopEdge)
              Positioned(
                left: 0,
                right: 0,
                top: 0,
                height: KasyDialogScrollableBody._fadeHeight,
                child: IgnorePointer(
                  child: AnimatedOpacity(
                    opacity: _showTopFade ? 1.0 : 0.0,
                    duration: const Duration(milliseconds: 200),
                    child: DecoratedBox(
                      decoration: BoxDecoration(
                        gradient: LinearGradient(
                          begin: Alignment.topCenter,
                          end: Alignment.bottomCenter,
                          colors: [
                            surfaceColor,
                            surfaceColor.withValues(alpha: 0),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            if (widget.fadeBottomEdge)
              Positioned(
                left: 0,
                right: 0,
                bottom: 0,
                height: KasyDialogScrollableBody._fadeHeight,
                child: IgnorePointer(
                  child: AnimatedOpacity(
                    opacity: _showBottomFade ? 1.0 : 0.0,
                    duration: const Duration(milliseconds: 200),
                    child: DecoratedBox(
                      decoration: BoxDecoration(
                        gradient: LinearGradient(
                          begin: Alignment.topCenter,
                          end: Alignment.bottomCenter,
                          colors: [
                            surfaceColor.withValues(alpha: 0),
                            surfaceColor,
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

class _DialogIconBubble extends StatelessWidget {
  final IconData icon;
  final KasyDialogIconTone tone;

  const _DialogIconBubble({required this.icon, required this.tone});

  static const double _size = 44;

  @override
  Widget build(BuildContext context) {
    final _DialogIconPalette p = _palette(context, tone);
    return Container(
      width: _size,
      height: _size,
      decoration: BoxDecoration(color: p.background, shape: BoxShape.circle),
      child: Icon(icon, size: 20, color: p.foreground),
    );
  }

  _DialogIconPalette _palette(BuildContext context, KasyDialogIconTone tone) {
    final KasyColors c = context.colors;
    final bool dark = context.isDark;
    return switch (tone) {
      KasyDialogIconTone.info => _DialogIconPalette(
        background: c.primary.withValues(alpha: dark ? 0.22 : 0.12),
        foreground: c.primary,
      ),
      KasyDialogIconTone.success => _DialogIconPalette(
        background: c.success.withValues(alpha: dark ? 0.22 : 0.12),
        foreground: c.success,
      ),
      KasyDialogIconTone.warning => _DialogIconPalette(
        background: c.warning.withValues(alpha: dark ? 0.22 : 0.12),
        foreground: c.warning,
      ),
      KasyDialogIconTone.danger => _DialogIconPalette(
        background: c.error.withValues(alpha: dark ? 0.22 : 0.12),
        foreground: c.error,
      ),
      KasyDialogIconTone.neutral => _DialogIconPalette(
        background: c.outline.withValues(alpha: dark ? 0.22 : 0.12),
        foreground: c.onSurface.withValues(alpha: 0.72),
      ),
    };
  }
}

class _DialogIconPalette {
  final Color background;
  final Color foreground;

  const _DialogIconPalette({
    required this.background,
    required this.foreground,
  });
}

/// Two-button confirm/cancel dialog built on [KasyDialog].
///
/// Feature code supplies copy and callbacks; layout stays in the design system.
/// Destructive flows use [showKasyBlurDialog] with a danger icon by default.
Future<void> showKasyConfirmDialog(
  BuildContext context, {
  required String title,
  String? message,
  required String cancelLabel,
  required String confirmLabel,
  VoidCallback? onCancel,
  VoidCallback? onConfirm,
  bool destructive = false,
  bool barrierDismissible = false,
  IconData? leadingIcon,
  KasyDialogIconTone iconTone = KasyDialogIconTone.warning,
}) {
  final Future<void> Function({
    required BuildContext context,
    required Widget Function(BuildContext dialogContext) builder,
    bool barrierDismissible,
  }) presentDialog = destructive ? showKasyBlurDialog<void> : showKasyDialog<void>;

  return presentDialog(
    context: context,
    barrierDismissible: barrierDismissible,
    builder: (dialogCtx) => KasyDialog(
      leadingIcon: leadingIcon ?? (destructive ? KasyIcons.trash : null),
      iconTone: destructive ? KasyDialogIconTone.danger : iconTone,
      title: title,
      titleCentered: leadingIcon == null && !destructive,
      message: message,
      showCloseButton: false,
      actionsAxis: Axis.horizontal,
      actions: [
        KasyButton(
          label: cancelLabel,
          variant: KasyButtonVariant.outline,
          expand: true,
          onPressed: () {
            Navigator.of(dialogCtx).pop();
            onCancel?.call();
          },
        ),
        KasyButton(
          label: confirmLabel,
          variant: destructive
              ? KasyButtonVariant.destructive
              : KasyButtonVariant.primary,
          expand: true,
          onPressed: () {
            Navigator.of(dialogCtx).pop();
            onConfirm?.call();
          },
        ),
      ],
    ),
  );
}

/// Shows a centered dialog using [showDialog]. Default barrier is semi-opaque dark;
/// pass [barrierColor] for a solid scrim (e.g. canvas tone via [showKasyScaffoldToneDialog]).
Future<T?> showKasyDialog<T>({
  required BuildContext context,
  required Widget Function(BuildContext dialogContext) builder,
  bool barrierDismissible = true,
  Color? barrierColor,
}) {
  final ThemeData theme = Theme.of(context);
  final bool dark = theme.brightness == Brightness.dark;
  return showDialog<T>(
    context: context,
    barrierDismissible: barrierDismissible,
    barrierColor:
        barrierColor ??
        Colors.black.withValues(alpha: dark ? 0.58 : 0.42),
    builder: (ctx) => Dialog(
      backgroundColor: Colors.transparent,
      elevation: 0,
      insetPadding: const EdgeInsets.symmetric(
        horizontal: KasySpacing.lg,
        vertical: KasySpacing.xl,
      ),
      child: builder(ctx),
    ),
  );
}

/// [showKasyDialog] with a solid barrier matching the scaffold / page canvas tone.
Future<T?> showKasyScaffoldToneDialog<T>({
  required BuildContext context,
  required Widget Function(BuildContext dialogContext) builder,
  bool barrierDismissible = true,
}) {
  return showKasyDialog<T>(
    context: context,
    barrierDismissible: barrierDismissible,
    barrierColor: Theme.of(context).scaffoldBackgroundColor,
    builder: builder,
  );
}

/// Entrance animation for the foreground content in [showKasyBlurDialog].
enum KasyBlurDialogTransitionStyle {
  /// Fade in while moving up from below (sheet-like on mobile).
  slideUpFade,
  /// Fade in with a slight scale-up around the layout center (legacy).
  scaleFade,
  /// Fade in with subtle scale, smooth entrance (recommended for input dialogs).
  scaleFadeSoft,
}

/// Centered dialog over a blurred, lightly dimmed backdrop (full-screen blur).
///
/// Foreground content is laid out in a [Center]; by default it **animates in**
/// with [KasyBlurDialogTransitionStyle.slideUpFade] (slide from below + fade).
/// Use [transitionStyle] for the previous scale + fade pop-in.
Future<T?> showKasyBlurDialog<T>({
  required BuildContext context,
  required Widget Function(BuildContext dialogContext) builder,
  bool barrierDismissible = true,
  double blurSigma = 14,
  KasyBlurDialogTransitionStyle transitionStyle =
      KasyBlurDialogTransitionStyle.slideUpFade,
}) {
  return showGeneralDialog<T>(
    context: context,
    barrierDismissible: barrierDismissible,
    barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
    barrierColor: Colors.transparent,
    transitionDuration: const Duration(milliseconds: 300),
    pageBuilder: (ctx, animation, _) => _BlurDialogScaffold(
      animation: animation,
      blurSigma: blurSigma,
      barrierDismissible: barrierDismissible,
      transitionStyle: transitionStyle,
      child: builder(ctx),
    ),
    transitionBuilder: (_, _, _, child) => child,
  );
}

class _BlurDialogScaffold extends StatefulWidget {
  final Animation<double> animation;
  final double blurSigma;
  final bool barrierDismissible;
  final KasyBlurDialogTransitionStyle transitionStyle;
  final Widget child;

  const _BlurDialogScaffold({
    required this.animation,
    required this.blurSigma,
    required this.barrierDismissible,
    required this.transitionStyle,
    required this.child,
  });

  @override
  State<_BlurDialogScaffold> createState() => _BlurDialogScaffoldState();
}

class _BlurDialogScaffoldState extends State<_BlurDialogScaffold> {
  double _bottomInset = 0;
  bool _isClosing = false;

  @override
  void initState() {
    super.initState();
    widget.animation.addStatusListener(_onStatusChange);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // Rastreia teclado subindo — congela quando fecha.
    if (!_isClosing) {
      _bottomInset = MediaQuery.viewInsetsOf(context).bottom;
    }
  }

  @override
  void dispose() {
    widget.animation.removeStatusListener(_onStatusChange);
    super.dispose();
  }

  void _onStatusChange(AnimationStatus status) {
    if (status == AnimationStatus.forward) {
      _isClosing = false;
    }
  }

  void _startClosingSequence(Object? result) {
    if (_isClosing) return;
    setState(() => _isClosing = true);
    Navigator.of(context).pop(result);
    Future.delayed(const Duration(milliseconds: 120), () {
      if (mounted) FocusScope.of(context).unfocus();
    });
  }

  Widget _buildForeground(Animation<double> curved, Widget child) {
    switch (widget.transitionStyle) {
      case KasyBlurDialogTransitionStyle.slideUpFade:
        return FadeTransition(
          opacity: curved,
          child: SlideTransition(
            position: Tween<Offset>(
              begin: const Offset(0, 0.08),
              end: Offset.zero,
            ).animate(curved),
            child: child,
          ),
        );
      case KasyBlurDialogTransitionStyle.scaleFade:
        return FadeTransition(
          opacity: curved,
          child: ScaleTransition(
            scale: Tween<double>(begin: 0.94, end: 1.0).animate(curved),
            child: child,
          ),
        );
      case KasyBlurDialogTransitionStyle.scaleFadeSoft:
        return FadeTransition(
          opacity: curved,
          child: ScaleTransition(
            scale: Tween<double>(begin: 0.98, end: 1.0).animate(curved),
            child: child,
          ),
        );
    }
  }

  @override
  Widget build(BuildContext context) {
    final curved = CurvedAnimation(
      parent: widget.animation,
      curve: Curves.easeOutQuart,
      reverseCurve: Curves.easeIn,
    );
    // Backdrop desaparece rapidamente na saída
    final backdropCurved = CurvedAnimation(
      parent: widget.animation,
      curve: Curves.linear,
      reverseCurve: Curves.easeOut,
    );
    final materialChild = Material(
      type: MaterialType.transparency,
      child: widget.child,
    );
    return PopScope(
      canPop: false,
      onPopInvokedWithResult: (didPop, result) {
        if (!didPop) _startClosingSequence(result);
      },
      child: AnimatedBuilder(
      animation: curved,
      builder: (ctx, _) {
        final double backdropT = backdropCurved.value;
        return Stack(
          fit: StackFit.expand,
          children: [
            GestureDetector(
              behavior: HitTestBehavior.opaque,
              onTap: widget.barrierDismissible && !_isClosing
                  ? () => _startClosingSequence(null)
                  : null,
              child: BackdropFilter(
                filter: ImageFilter.blur(
                  sigmaX: widget.blurSigma * backdropT,
                  sigmaY: widget.blurSigma * backdropT,
                ),
                child: ColoredBox(
                  color: Colors.black.withValues(alpha: 0.28 * backdropT),
                  child: const SizedBox.expand(),
                ),
              ),
            ),
            Padding(
              padding: EdgeInsets.only(
                left: KasySpacing.lg,
                right: KasySpacing.lg,
                top: KasySpacing.xl,
                bottom: KasySpacing.xl + _bottomInset,
              ),
              child: Center(
                child: _buildForeground(curved, materialChild),
              ),
            ),
          ],
        );
      },
      ),
    );
  }
}
