import 'dart:io';

import 'package:baseproject/core/common/utils.dart';
import 'package:baseproject/core/components/form/form_builder_field.dart';
import 'package:baseproject/core/language/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_datetime_picker_plus/flutter_datetime_picker_plus.dart';
import 'package:intl/intl.dart';
import 'dart:ui' as ui;

enum InputType { date, time, both }

// ignore: must_be_immutable
class DateTimePicker extends FormBuilderField<DateTime> {
  /// The date/time picker dialogs to show.
  final InputType inputType;

  /// Allow manual editing of the date/time. Defaults to true. If false, the
  /// picker(s) will be shown every time the field gains focus.
  // final bool editable;

  /// For representing the date as a string e.g.
  /// `DateFormat("EEEE, MMMM d, yyyy 'at' h:mma")`
  /// (Sunday, June 3, 2018 at 9:24pm)
  final DateFormat? format;

  /// The date the calendar opens to when displayed. Defaults to the current date.
  ///
  /// To preset the widget's value, use [initialValue] instead.
  final DateTime? initialDate;

  /// The earliest choosable date. Defaults to 1900.
  final DateTime? firstDate;

  /// The latest choosable date. Defaults to 2100.
  final DateTime? lastDate;

  final DateTime? currentDate;

  /// The initial time prefilled in the picker dialog when it is shown. Defaults
  /// to noon. Explicitly set this to `null` to use the current time.
  final TimeOfDay initialTime;

  /// If defined, the TextField [decoration]'s [suffixIcon] will be
  /// overridden to reset the input using the icon defined here.
  /// Set this to `null` to stop that behavior. Defaults to [Icons.close].
  final Icon resetIcon;

  /// Called when an enclosing form is saved. The value passed will be `null`
  /// if [format] fails to parse the text.
  // final FormFieldSetter<DateTime> onSaved;

  /// Corresponds to the [showDatePicker()] parameter. Defaults to
  /// [DatePickerMode.day].
  final DatePickerMode initialDatePickerMode;

  /// Corresponds to the [showDatePicker()] parameter.
  ///
  /// See [GlobalMaterialLocalizations](https://docs.flutter.io/flutter/flutter_localizations/GlobalMaterialLocalizations-class.html)
  /// for acceptable values.
  final Locale? locale;

  /// Corresponds to the [showDatePicker()] parameter.
  final ui.TextDirection? textDirection;

  /// Corresponds to the [showDatePicker()] parameter.
  final bool useRootNavigator;

  /// Called when an enclosing form is submitted. The value passed will be
  /// `null` if [format] fails to parse the text.
  final ValueChanged<DateTime?>? onFieldSubmitted;
  final TextEditingController? controller;
  final TextInputType keyboardType;
  final TextStyle? style;
  final TextAlign textAlign;

  /// Preset the widget's value.
  final bool autofocus;
  final bool obscureText;
  final bool autocorrect;
  final MaxLengthEnforcement maxLengthEnforcement;
  final int? maxLines;
  final int? maxLength;
  final List<TextInputFormatter>? inputFormatters;
  final TransitionBuilder? transitionBuilder;

  /// Called whenever the state's value changes, e.g. after picker value(s)
  /// have been selected or when the field loses focus. To listen for all text
  /// changes, use the [controller] and [focusNode].
  // final ValueChanged<DateTime>? onChanged;

  final bool showCursor;

  final int? minLines;

  final bool expands;

  final TextInputAction? textInputAction;

  final VoidCallback? onEditingComplete;

  final InputCounterWidgetBuilder? buildCounter;

  // final VoidCallback onEditingComplete,
  final Radius? cursorRadius;
  final Color? cursorColor;
  final Brightness? keyboardAppearance;
  final EdgeInsets scrollPadding;
  final bool enableInteractiveSelection;

  final double cursorWidth;
  final TextCapitalization textCapitalization;
  final bool alwaysUse24HourFormat;

  final String? cancelText;
  final String? confirmText;
  final String? errorFormatText;
  final String? errorInvalidText;
  final String? fieldHintText;
  final String? fieldLabelText;
  final String? helpText;
  final DatePickerEntryMode initialEntryMode;
  final RouteSettings? routeSettings;

  final TimePickerEntryMode timePickerInitialEntryMode;
  final StrutStyle? strutStyle;
  final SelectableDayPredicate? selectableDayPredicate;
  final double? sizeHeight;

  final bool isShowAfterTime;

  DateTimePicker(
      {Key? key,
      //From Super
      required String name,
      FormFieldValidator<DateTime>? validator,
      DateTime? initialValue,
      InputDecoration decoration = const InputDecoration(),
      ValueChanged<DateTime?>? onChanged,
      ValueTransformer<DateTime?>? valueTransformer,
      bool enabled = true,
      FormFieldSetter<DateTime>? onSaved,
      AutovalidateMode autovalidateMode = AutovalidateMode.disabled,
      VoidCallback? onReset,
      FocusNode? focusNode,
      this.inputType = InputType.both,
      this.scrollPadding = const EdgeInsets.all(20.0),
      this.cursorWidth = 2.0,
      this.enableInteractiveSelection = true,
      this.resetIcon = const Icon(Icons.close),
      this.initialTime = const TimeOfDay(hour: 12, minute: 0),
      this.keyboardType = TextInputType.text,
      this.textAlign = TextAlign.start,
      this.autofocus = false,
      this.obscureText = false,
      this.autocorrect = true,
      this.maxLines = 1,
      this.expands = false,
      this.initialDatePickerMode = DatePickerMode.day,
      this.transitionBuilder,
      this.textCapitalization = TextCapitalization.none,
      this.useRootNavigator = true,
      this.alwaysUse24HourFormat = false,
      this.initialEntryMode = DatePickerEntryMode.calendar,
      this.timePickerInitialEntryMode = TimePickerEntryMode.dial,
      this.format,
      this.initialDate,
      this.firstDate,
      this.lastDate,
      this.currentDate,
      this.locale,
      this.maxLength,
      this.textDirection,
      this.onFieldSubmitted,
      this.controller,
      this.style,
      this.maxLengthEnforcement = MaxLengthEnforcement.none,
      this.inputFormatters,
      this.showCursor = false,
      this.minLines,
      this.textInputAction,
      this.onEditingComplete,
      this.buildCounter,
      this.cursorRadius,
      this.cursorColor,
      this.keyboardAppearance,
      this.cancelText,
      this.confirmText,
      this.errorFormatText,
      this.errorInvalidText,
      this.fieldHintText,
      this.fieldLabelText,
      this.helpText,
      this.routeSettings,
      this.strutStyle,
      this.selectableDayPredicate,
      this.sizeHeight,
      this.isShowAfterTime = true,
      Widget? leftIcon,
      Widget rightIcon = const Icon(
        Icons.calendar_today_outlined,
        size: 18,
      ),
      Color? backgroundColor,
      EdgeInsets? contentPadding})
      : super(
          key: key,
          initialValue: initialValue,
          name: name,
          validator: validator,
          valueTransformer: valueTransformer,
          onChanged: onChanged,
          autovalidateMode: autovalidateMode,
          onSaved: onSaved,
          enabled: enabled,
          onReset: onReset,
          decoration: decoration,
          focusNode: focusNode,
          builder: (FormFieldState<DateTime?> field) {
            final _DateTimePickerState state = field as _DateTimePickerState;

            late InputDecoration decoration =
                state.decoration().copyWith(border: OutlineInputBorder(borderRadius: BorderRadius.circular(70)));

            if (backgroundColor != null) decoration = decoration.copyWith(filled: true, fillColor: backgroundColor);
            if (contentPadding != null) decoration = decoration.copyWith(contentPadding: contentPadding);
            if (leftIcon != null) decoration = decoration.copyWith(prefixIcon: leftIcon);
            decoration = decoration
                .copyWith(
                  suffixIcon: rightIcon,
                  suffixIconConstraints: const BoxConstraints(minHeight: 40, minWidth: 40),
                )
                .copyWith(
                    errorBorder: OutlineInputBorder(
                  borderSide: BorderSide(color: Colors.red),
                ));

            return Container(
              alignment: Alignment.center,
              // height: sizeHeight ?? 52,
              child: TextField(
                textDirection: textDirection,
                textAlign: textAlign,
                maxLength: maxLength,
                autofocus: autofocus,
                decoration: decoration,
                readOnly: true,
                enabled: state.enabled,
                autocorrect: autocorrect,
                controller: state._textFieldController,
                focusNode: state.effectiveFocusNode,
                inputFormatters: inputFormatters,
                keyboardType: keyboardType,
                maxLines: maxLines,
                obscureText: obscureText,
                showCursor: showCursor,
                minLines: minLines,
                expands: expands,
                style: style,
                onEditingComplete: onEditingComplete,
                buildCounter: buildCounter,
                cursorColor: cursorColor,
                cursorRadius: cursorRadius,
                cursorWidth: cursorWidth,
                enableInteractiveSelection: enableInteractiveSelection,
                keyboardAppearance: keyboardAppearance,
                scrollPadding: scrollPadding,
                strutStyle: strutStyle,
                textCapitalization: textCapitalization,
                textInputAction: textInputAction,
                maxLengthEnforcement: maxLengthEnforcement,
              ),
            );
          },
        );

  @override
  _DateTimePickerState createState() => _DateTimePickerState();
}

class _DateTimePickerState extends FormBuilderFieldState<DateTimePicker, DateTime> {
  late TextEditingController _textFieldController;
  late bool _isShowAfterTime;
  late DateFormat _dateFormat;

  @override
  void initState() {
    super.initState();
    _isShowAfterTime = widget.isShowAfterTime;
    _textFieldController = widget.controller ?? TextEditingController();
    _dateFormat = widget.format ?? _getDefaultDateTimeFormat();
    final DateTime? initVal = initialValue;
    _textFieldController.text = (initVal == null || (initVal.year == 1)) ? '' : _dateFormat.format(initVal);
    effectiveFocusNode!.addListener(_handleFocus);
  }

  @override
  void didUpdateWidget(covariant FormField<DateTime?> oldWidget) {
    if (oldWidget.initialValue != widget.initialValue) {
      setValue(widget.initialValue);
      setState(() {
        final DateTime? initVal = initialValue;
        _textFieldController.text = initVal == null ? '' : _dateFormat.format(initVal);
      });
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    effectiveFocusNode!.removeListener(_handleFocus);
    // Dispose the _textFieldController when initState created it
    if (null == widget.controller) {
      _textFieldController.dispose();
    }
    super.dispose();
  }

  Future<void> _handleFocus() async {
    if (effectiveFocusNode!.hasFocus && enabled) {
      effectiveFocusNode!.unfocus();
      await onShowPicker(context, value);
    }
  }

  DateFormat _getDefaultDateTimeFormat() {
    final String? languageCode = widget.locale?.languageCode;
    switch (widget.inputType) {
      case InputType.time:
        return DateFormat.Hm(languageCode);
      case InputType.date:
        return DateFormat.yMd(languageCode);
      case InputType.both:
      default:
        return DateFormat.Hm(languageCode).add_yMd();
    }
  }

  Future<DateTime?> onShowPicker(BuildContext context, DateTime? currentValue) async {
    // ignore: parameter_assignments
    currentValue = value;
    DateTime? newValue;
    switch (widget.inputType) {
      case InputType.date:
        newValue = await _showDatePicker(context, currentValue);
        break;
      case InputType.time:
        final TimeOfDay? newTime = await _showTimePicker(context, currentValue);
        newValue = null != newTime ? convert(newTime) : null;
        break;
      case InputType.both:
        final DateTime? date = await _showDatePicker(context, currentValue);
        if (date != null) {
          final TimeOfDay? time = await _showTimePicker(context, currentValue);
          newValue = combine(date, time);
        }
        break;
      default:
        throw 'Unexpected input type ${widget.inputType}';
    }
    DateTime? finalValue = newValue ?? currentValue;
    if (finalValue != null) {
      if (widget.inputType == InputType.date) finalValue = DateTime(finalValue.year, finalValue.month, finalValue.day);
    }
    didChange(finalValue);
    return finalValue;
  }

  Future<DateTime?> _showDatePicker(BuildContext context, DateTime? currentValue) {
    final DateTime _minDate =
        _isShowAfterTime != null && !_isShowAfterTime ? DateTime.now() : widget.firstDate ?? DateTime(1900);
    if (Platform.isIOS) {
      return DatePicker.showDatePicker(
        context,
        minTime: _minDate,
        maxTime: widget.lastDate ?? DateTime(2100),
        currentTime: currentValue,
        locale: getLocaleType(),
      );
    }

    return showDatePicker(
      context: context,
      selectableDayPredicate: widget.selectableDayPredicate,
      initialDatePickerMode: widget.initialDatePickerMode,
      initialDate: currentValue ?? widget.initialDate ?? DateTime.now(),
      firstDate: _minDate,
      lastDate: widget.lastDate ?? DateTime(2100),
      locale: widget.locale,
      textDirection: widget.textDirection,
      useRootNavigator: widget.useRootNavigator,
      builder: widget.transitionBuilder,
      cancelText: widget.cancelText,
      confirmText: widget.confirmText,
      errorFormatText: widget.errorFormatText,
      errorInvalidText: widget.errorInvalidText,
      fieldHintText: widget.fieldHintText,
      fieldLabelText: widget.fieldLabelText,
      helpText: widget.helpText,
      initialEntryMode: widget.initialEntryMode,
      routeSettings: widget.routeSettings,
      currentDate: widget.currentDate,
    );
  }

  Future<TimeOfDay?> _showTimePicker(BuildContext context, DateTime? currentValue) async {
    if (Platform.isIOS) {
      final DateTime? result = await DatePicker.showTimePicker(
        context,
        currentTime: currentValue,
        locale: getLocaleType(),
      );

      if (result == null) return null;
      return TimeOfDay.fromDateTime(result);
    }

    final TimeOfDay? timePickerResult = await showTimePicker(
      context: context,
      initialTime: currentValue != null ? TimeOfDay.fromDateTime(currentValue) : widget.initialTime,
      builder: widget.transitionBuilder,
      useRootNavigator: widget.useRootNavigator,
      routeSettings: widget.routeSettings,
      initialEntryMode: widget.timePickerInitialEntryMode,
      helpText: widget.helpText,
      confirmText: widget.confirmText,
      cancelText: widget.cancelText,
    );
    return timePickerResult ?? (currentValue != null ? TimeOfDay.fromDateTime(currentValue) : null);
  }

  LocaleType getLocaleType() {
    return Utils.enumFromString<LocaleType>(LocaleType.values, AppLocalizations.of(context)!.locale.languageCode);
  }

  /// Sets the hour and minute of a [DateTime] from a [TimeOfDay].
  DateTime combine(DateTime date, TimeOfDay? time) =>
      DateTime(date.year, date.month, date.day, time?.hour ?? 0, time?.minute ?? 0);

  DateTime? convert(TimeOfDay? time) => time == null ? null : DateTime(1, 1, 1, time.hour, time.minute);

  @override
  void didChange(DateTime? val, {bool runOnChange = true}) {
    super.didChange(
      val,
    );
    _textFieldController.text = (val == null) ? '' : _dateFormat.format(val);
  }
}
