// 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 'dart:ui';

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.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/text_inputs/cubit/text_input_cubit.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/text_input_theme_resolver.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/widgets/label_widget.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/widgets/text_input_wrapper.dart';

class TextInputScreen extends CubitScreen<TextInputCubit, TextInputState> {
  TextInputScreen({
    this.expand,
    this.onError,
    this.validator,
    super.key,
    this.suffixText,
    this.prefixText,
    this.prefixIcon,
    this.suffixIcon,
    this.label,
    this.hint,
    this.normalStyle,
    this.focusedStyle,
    this.errorStyle,
    this.disableStyle,
    this.magnifierConfiguration,
    this.controller,
    this.focusNode,
    this.keyboardType,
    this.textInputAction,
    this.textCapitalization,
    this.style,
    this.strutStyle,
    this.textAlign,
    this.textAlignVertical,
    this.textDirection,
    this.autofocus,
    this.obscuringCharacter,
    this.obscureText,
    this.autocorrect,
    this.smartDashesType,
    this.smartQuotesType,
    this.enableSuggestions,
    this.maxLines,
    this.minLines,
    this.expands,
    this.readOnly,
    this.showCursor,
    this.maxLength,
    this.maxLengthEnforcement,
    this.onChanged,
    this.onEditingComplete,
    this.onSubmitted,
    this.onAppPrivateCommand,
    this.inputFormatters,
    this.enabled,
    this.cursorWidth,
    this.cursorHeight,
    this.cursorRadius,
    this.cursorColor,
    this.selectionHeightStyle,
    this.selectionWidthStyle,
    this.keyboardAppearance,
    this.scrollPadding,
    this.enableInteractiveSelection,
    this.selectionControls,
    this.dragStartBehavior,
    this.onTap,
    this.onTapOutside,
    this.mouseCursor,
    this.scrollPhysics,
    this.scrollController,
    this.autofillHints,
    this.clipBehavior,
    this.restorationId,
    this.scribbleEnabled,
    this.enableIMEPersonalizedLearning,
    this.contextMenuBuilder,
    this.spellCheckConfiguration,
  });

  final TextMagnifierConfiguration? magnifierConfiguration;
  final TextEditingController? controller;
  final FocusNode? focusNode;
  final TextInputType? keyboardType;
  final TextInputAction? textInputAction;
  final TextCapitalization? textCapitalization;
  final TextStyle? style;
  final StrutStyle? strutStyle;
  final TextAlign? textAlign;
  final TextAlignVertical? textAlignVertical;
  final TextDirection? textDirection;
  final bool? autofocus;
  final String? obscuringCharacter;
  final bool? obscureText;
  final bool? autocorrect;
  final SmartDashesType? smartDashesType;
  final SmartQuotesType? smartQuotesType;
  final bool? enableSuggestions;
  final int? maxLines;
  final int? minLines;
  final bool? expands;
  final bool? readOnly;
  final bool? showCursor;
  final int? maxLength;
  final MaxLengthEnforcement? maxLengthEnforcement;
  final ValueChanged<String>? onChanged;
  final VoidCallback? onEditingComplete;
  final ValueChanged<String>? onSubmitted;
  final AppPrivateCommandCallback? onAppPrivateCommand;
  final List<TextInputFormatter>? inputFormatters;
  final double? cursorWidth;
  final double? cursorHeight;
  final Radius? cursorRadius;
  final Color? cursorColor;
  final BoxHeightStyle? selectionHeightStyle;
  final BoxWidthStyle? selectionWidthStyle;
  final Brightness? keyboardAppearance;
  final EdgeInsets? scrollPadding;
  final bool? enableInteractiveSelection;
  final TextSelectionControls? selectionControls;
  final DragStartBehavior? dragStartBehavior;
  final GestureTapCallback? onTap;
  final TapRegionCallback? onTapOutside;
  final MouseCursor? mouseCursor;
  final ScrollPhysics? scrollPhysics;
  final ScrollController? scrollController;
  final Iterable<String>? autofillHints;
  final Clip? clipBehavior;
  final String? restorationId;
  final bool? scribbleEnabled;
  final bool? enableIMEPersonalizedLearning;
  final EditableTextContextMenuBuilder? contextMenuBuilder;
  final SpellCheckConfiguration? spellCheckConfiguration;

  final bool Function(String)? validator;
  final String? Function(String)? onError;

  final TextInputStyle? normalStyle;
  final TextInputStyle? focusedStyle;
  final TextInputStyle? errorStyle;
  final TextInputStyle? disableStyle;

  final TextWrapper? label;
  final TextWrapper? hint;
  final ValueNotifier<bool>? enabled;

  final bool? expand;

  final TextWrapper? prefixText;
  final Icon? prefixIcon;
  final Icon? suffixIcon;
  final TextWrapper? suffixText;

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

  @override
  TextInputCubit init(BuildContext context, TextInputCubit bloc) {
    enabled?.addListener(() {
      if (enabled?.value ?? false) {
        bloc.enable();
      } else {
        bloc.disable();
      }
    });
    return bloc;
  }

  final _focusNode = FocusNode();
  final _controller = TextEditingController();

  final _notOutilinedBorder = const OutlineInputBorder(
    borderSide: BorderSide(color: Colors.transparent),
  );

  TextInputStyle _resolve(BuildContext context, TextInputState state) {
    final resolver = TextInputThemeResolver(
      customStyleFn: (context, {extra}) {
        TextInputStyle? textInputStyle;
        switch (extra?.controlState) {
          case ControlState.focused:
            textInputStyle = focusedStyle;
            break;
          case ControlState.disabled:
            textInputStyle = disableStyle;
            break;
          case ControlState.normal:
            textInputStyle = normalStyle;
            break;
          case ControlState.hovered:
          case ControlState.tapped:
          case null:
            break;
        }

        TextInputStyle? style;
        switch (extra?.statusState) {
          case StatusState.error:
            style = errorStyle;
            break;
          case StatusState.initial:
          case StatusState.success:
          case StatusState.loading:
          case null:
            break;
        }

        return TextInputStyle.merge(textInputStyle, style);
      },
    );

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

  bool _wrapperExpanded(TextInputState state) {
    final fn = focusNode ?? _focusNode;
    final tec = controller ?? _controller;
    if (fn.hasFocus && label != null) {
      return true;
    } else if (tec.value.text.isNotEmpty && label != null) {
      return true;
    } else if (state.statusState == StatusState.error &&
        (state.statusMessage?.isNotEmpty ?? false)) {
      return true;
    }
    return false;
  }

  @override
  Widget onBuild(BuildContext context, TextInputState state) {
    final style = _resolve(context, state);
    return Focus(
      descendantsAreFocusable: true,
      onFocusChange: (hasFocus) {
        if (hasFocus) {
          bloc(context).onFocus();
          (focusNode ?? _focusNode).requestFocus();
        } else {
          bloc(context).onUnfocus();
        }
      },
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 600),
        decoration: BoxDecoration(
          boxShadow: style.boxShadow != null ? [style.boxShadow!] : null,
          gradient: style.backgroundColors?.isGradient ?? false
              ? LinearGradient(colors: style.backgroundColors!.colors)
              : null,
          color: style.backgroundColors?.isColor ?? false
              ? style.backgroundColors?.color
              : null,
          borderRadius: style.radius ?? BorderRadius.circular(4),
          border: (style.borderColors != null)
              ? Border.all(
                  width: 1.5,
                  color: style.borderColors!,
                )
              : null,
        ),
        child: TextInputWrapper(
          expand: expand ?? true,
          expanded: _wrapperExpanded(state),
          child: TextField(
            focusNode: focusNode ?? _focusNode,
            onTap: onTap,
            onChanged: (value) {
              onChanged?.call(value);
              if (validator?.call(value) ?? false) {
                bloc(context).onInvalid(onError?.call(value));
              } else {
                bloc(context).onSuccess();
              }
            },
            onTapOutside: onTapOutside,
            controller: controller ?? _controller,
            textAlignVertical: textAlignVertical ?? TextAlignVertical.top,
            style: style.inputStyle ?? this.style,
            decoration: InputDecoration(
              focusedErrorBorder: _notOutilinedBorder,
              focusedBorder: _notOutilinedBorder,
              errorBorder: _notOutilinedBorder,
              disabledBorder: _notOutilinedBorder,
              enabledBorder: _notOutilinedBorder,
              border: _notOutilinedBorder,
              isDense: true,
              iconColor: style.iconColor,
              alignLabelWithHint: true,
              label: (state.statusState == StatusState.error &&
                          (state.statusMessage?.isNotEmpty ?? false)) ||
                      label != null
                  ? LabelWidget(
                      focusNode: focusNode ?? _focusNode,
                      label: (state.statusState == StatusState.error &&
                              (state.statusMessage?.isNotEmpty ?? false))
                          ? state.statusMessage?.wrap()
                          : label,
                      labelStyle: style.labelStyle,
                    )
                  : null,
              hintText: hint?.data,
              hintStyle: hint?.style,
              prefixIcon: prefixIcon,
              prefixText: prefixText?.data,
              prefixStyle: prefixText?.style,
              prefixIconColor: style.prefixIconColor,
              suffixIcon: suffixIcon,
              suffixText: suffixText?.data,
              suffixStyle: suffixText?.style,
              suffixIconColor: style.suffixIconColor,
              enabled: state.controlState != ControlState.disabled,
            ),
            keyboardType: keyboardType,
            smartDashesType: smartDashesType,
            smartQuotesType: smartQuotesType,
            enableInteractiveSelection: enableInteractiveSelection,
            textInputAction: textInputAction,
            textCapitalization: textCapitalization ?? TextCapitalization.none,
            strutStyle: strutStyle,
            textAlign: textAlign ?? TextAlign.start,
            textDirection: textDirection,
            readOnly: readOnly ?? false,
            showCursor: showCursor,
            autofocus: autofocus ?? false,
            obscuringCharacter: obscuringCharacter ?? '*',
            obscureText: obscureText ?? false,
            autocorrect: autocorrect ?? true,
            enableSuggestions: enableSuggestions ?? true,
            maxLines: maxLines ?? 1,
            minLines: minLines,
            expands: expands ?? false,
            maxLength: maxLength,
            maxLengthEnforcement: maxLengthEnforcement,
            onEditingComplete: onEditingComplete,
            onSubmitted: onSubmitted,
            onAppPrivateCommand: onAppPrivateCommand,
            inputFormatters: inputFormatters,
            enabled: state.controlState != ControlState.disabled,
            cursorWidth: cursorWidth ?? 2.0,
            cursorHeight: cursorHeight,
            cursorRadius: cursorRadius,
            cursorColor: cursorColor,
            selectionHeightStyle: selectionHeightStyle ?? BoxHeightStyle.tight,
            selectionWidthStyle: selectionWidthStyle ?? BoxWidthStyle.tight,
            keyboardAppearance: keyboardAppearance,
            scrollPadding: scrollPadding ?? const EdgeInsets.all(20),
            dragStartBehavior: dragStartBehavior ?? DragStartBehavior.start,
            selectionControls: selectionControls,
            mouseCursor: mouseCursor,
            scrollController: scrollController,
            scrollPhysics: scrollPhysics,
            autofillHints: autofillHints,
            clipBehavior: clipBehavior ?? Clip.hardEdge,
            restorationId: restorationId,
            scribbleEnabled: scribbleEnabled ?? true,
            enableIMEPersonalizedLearning:
                enableIMEPersonalizedLearning ?? true,
            contextMenuBuilder: contextMenuBuilder,
            spellCheckConfiguration: spellCheckConfiguration,
            magnifierConfiguration: magnifierConfiguration,
          ),
        ),
      ),
    );
  }
}
