// 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/selectable_button_cubit.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/state_listener.dart';
import 'package:wyatt_ui_kit/src/components/buttons/symbol_button/symbol_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 SymbolButtonScreen
    extends CubitScreen<SelectableButtonCubit, ButtonState> {
  const SymbolButtonScreen({
    this.icon,
    this.label,
    this.disabledStyle,
    this.normalStyle,
    this.hoveredStyle,
    this.focusedStyle,
    this.tappedStyle,
    this.selectedStyle,
    this.onPressed,
    this.disabled,
    this.mainAxisSize,
    this.themeResolver,
    super.key,
  });

  final Widget? icon;
  final TextWrapper? label;
  final MainAxisSize? mainAxisSize;

  final SymbolButtonStyle? disabledStyle;
  final SymbolButtonStyle? normalStyle;
  final SymbolButtonStyle? hoveredStyle;
  final SymbolButtonStyle? focusedStyle;
  final SymbolButtonStyle? tappedStyle;
  final SymbolButtonStyle? selectedStyle;

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

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

  @override
  SelectableButtonCubit init(BuildContext context, SelectableButtonCubit bloc) {
    disabled?.addListener(
      () => StateListener.listen<SelectableButtonCubit>(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.
  SymbolButtonStyle _resolve(BuildContext context, ButtonState state) {
    final SymbolButtonThemeResolver resolver = themeResolver ??
        SymbolButtonThemeResolver(
          customStyleFn: (context, {extra}) {
            SymbolButtonStyle? style;
            switch (extra?.state) {
              case ControlState.disabled:
                style = disabledStyle;
                break;
              case ControlState.focused:
                style = focusedStyle;
                break;
              case ControlState.hovered:
                style = hoveredStyle;
                break;
              case ControlState.tapped:
                style = tappedStyle;
                break;
              case ControlState.normal:
              case null:
                style = normalStyle;
                break;
            }
            if (extra?.isSelected ?? false) {
              style = selectedStyle;
            }

            return style;
          },
        );
    return resolver.negotiate(context, extra: state);
  }

  @override
  Widget onBuild(BuildContext context, ButtonState state) {
    final style = _resolve(context, 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(
          behavior: HitTestBehavior.opaque,
          onTapDown: (details) {
            bloc(context).onClickDown();
          },
          onTapUp: (details) {
            onPressed?.call(state.state);
            bloc(context).onClickUpIn();
          },
          onTapCancel: () {
            onPressed?.call(state.state);
            bloc(context).onClickUpOut();
          },
          child: Row(
            mainAxisSize: mainAxisSize ?? MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              SizedBox.square(
                dimension: style.dimension,
                child: AspectRatio(
                  aspectRatio: 1,
                  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: BoxConstraints(
                        minWidth: style.dimension ?? 60,
                      ), // min sizes for Material buttons
                      child: Padding(
                        padding: style.padding ?? EdgeInsets.zero,
                        child: Center(child: icon),
                      ),
                    ),
                  ),
                ),
              ),

              /// Choose color
              /// label.style.color ??
              ///  buttonStyle.label.style.color ??
              ///    context.textTheme.labelLarge.color
              ///
              /// Choose gradient
              /// label.gradient ??
              ///  buttonStyle.foregroundColor.colors ??
              ///    null
              ///
              /// More infos in `negociate()` method
              if (label != null) ...[
                Gap(style.padding?.horizontal ?? 10),
                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,
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}
