// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'package:flutter/material.dart' hide ButtonStyle;
import 'package:flutter/services.dart';
import 'package:gap/gap.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
import 'package:wyatt_ui_components/wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/state_listener.dart';
import 'package:wyatt_ui_kit/src/components/buttons/flat_button/flat_button_theme_resolver.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart';

class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
  const FlatButtonScreen({
    this.prefix,
    this.suffix,
    this.label,
    this.disabledStyle,
    this.normalStyle,
    this.hoveredStyle,
    this.focusedStyle,
    this.tappedStyle,
    this.onPressed,
    this.disabled,
    this.mainAxisSize,
    this.themeResolver,
    super.key,
  });

  final Widget? prefix;
  final Widget? suffix;
  final TextWrapper? label;
  final MainAxisSize? mainAxisSize;

  final FlatButtonStyle? disabledStyle;
  final FlatButtonStyle? normalStyle;
  final FlatButtonStyle? hoveredStyle;
  final FlatButtonStyle? focusedStyle;
  final FlatButtonStyle? tappedStyle;

  final void Function(ControlState state)? onPressed;
  final ValueNotifier<bool>? disabled;
  final FlatButtonThemeResolver? themeResolver;

  @override
  ButtonCubit create(BuildContext context) => ButtonCubit();

  @override
  ButtonCubit init(BuildContext context, ButtonCubit bloc) {
    disabled?.addListener(() => StateListener.listen(disabled, bloc));

    /// Set the initial state depending on the disabled value
    /// after adding the listener.
    if (disabled?.value ?? false) {
      bloc.disable();
    }
    return bloc;
  }

  /// Negotiate the theme to get a complete style.
  FlatButtonStyle _resolve(BuildContext context, ControlState state) {
    final FlatButtonThemeResolver resolver = themeResolver ??
        FlatButtonThemeResolver(
          customStyleFn: (context, {extra}) {
            switch (extra) {
              case ControlState.disabled:
                return disabledStyle;
              case ControlState.focused:
                return focusedStyle;
              case ControlState.hovered:
                return hoveredStyle;
              case ControlState.tapped:
                return tappedStyle;
              case ControlState.normal:
              case null:
                return normalStyle;
            }
          },
        );
    return resolver.negotiate(context, extra: state);
  }

  @override
  Widget onBuild(BuildContext context, ButtonState state) {
    final style = _resolve(context, state.state);

    return Focus(
      onFocusChange: (hasFocus) =>
          hasFocus ? bloc(context).onFocus() : bloc(context).onUnfocus(),
      onKeyEvent: (focusNode, event) {
        if (event.logicalKey == LogicalKeyboardKey.enter && state.isFocused) {
          onPressed?.call(state.state);
          bloc(context).onClickUpOut();
          return KeyEventResult.handled;
        }
        return KeyEventResult.ignored;
      },
      child: MouseRegion(
        cursor: SystemMouseCursors.click,
        onEnter: (event) {
          bloc(context).onMouseEnter();
        },
        onExit: (event) {
          bloc(context).onMouseLeave();
        },
        child: GestureDetector(
          onTapDown: (details) {
            bloc(context).onClickDown();
          },
          onTapUp: (details) {
            onPressed?.call(state.state);
            bloc(context).onClickUpIn();
          },
          onTapCancel: () {
            onPressed?.call(state.state);
            bloc(context).onClickUpOut();
          },
          child: DecoratedBox(
            decoration: BoxDecoration(
              color: style.backgroundColors?.color,
              // If no border color => no default value
              border: (style.borderColors != null && style.stroke != null)
                  ? (style.borderColors?.isGradient ?? false)
                      ? GradientBoxBorder(
                          gradient: LinearGradient(
                            colors: style.borderColors!.colors,
                          ),
                          width: style.stroke!,
                        )
                      : Border.all(
                          color: style.borderColors!.color,
                          width: style.stroke!,
                        )
                  : null,
              // if no gradient colors => no default value
              gradient: (style.backgroundColors?.isGradient ?? false)
                  ? LinearGradient(
                      colors: style.backgroundColors!.colors,
                    )
                  : null,
              boxShadow: [
                if (style.shadow != null) ...[style.shadow!]
              ],
              borderRadius: style.radius,
            ),
            child: ConstrainedBox(
              constraints: const BoxConstraints(
                minWidth: 88,
                minHeight: 36,
              ), // min sizes for Material buttons
              child: Padding(
                padding: style.padding ?? EdgeInsets.zero,
                child: Row(
                  mainAxisSize: mainAxisSize ?? MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    if (style.foregroundColors?.color != null &&
                        prefix != null) ...[
                      ColorFiltered(
                        colorFilter: ColorFilter.mode(
                          style.foregroundColors!.color,
                          BlendMode.srcIn,
                        ),
                        child: prefix,
                      )
                    ] else ...[
                      prefix ?? const SizedBox.shrink()
                    ],
                    Gap(style.padding?.vertical ?? 10),

                    /// Choose color
                    ///  buttonStyle.label.style.color ??
                    ///    context.textTheme.labelLarge.color
                    ///
                    /// Choose gradient
                    /// label.gradient ??
                    ///  buttonStyle.foregroundColor.colors ??
                    ///    null
                    ///
                    /// More infos in ThemeResolver class
                    if (label != null) ...[
                      Text(
                        label!.data,
                        style: label!.style ?? style.label,
                        textAlign: label!.textAlign,
                        textDirection: label!.textDirection,
                        softWrap: label!.softWrap,
                        overflow: label!.overflow,
                        maxLines: label!.maxLines,
                        selectionColor: label!.selectionColor,
                      ).toGradient(
                        gradientColors: style.foregroundColors,
                      )
                    ],
                    Gap(style.padding?.vertical ?? 10),
                    if (style.foregroundColors?.color != null &&
                        suffix != null) ...[
                      ColorFiltered(
                        colorFilter: ColorFilter.mode(
                          style.foregroundColors!.color,
                          BlendMode.srcIn,
                        ),
                        child: suffix,
                      )
                    ] else ...[
                      suffix ?? const SizedBox.shrink()
                    ],
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
