import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart';

class BolusDetailScreen extends StatefulWidget {
  static const String routeName = '/bolus';

  final int bolusProfileId;
  final int id;
  final TimeOfDay? suggestedStartTime;
  final TimeOfDay? suggestedEndTime;

  const BolusDetailScreen(
      {Key? key,
      this.bolusProfileId = 0,
      this.id = 0,
      this.suggestedStartTime,
      this.suggestedEndTime})
      : super(key: key);

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

class _BolusDetailScreenState extends State<BolusDetailScreen> {
  Bolus? _bolus;
  bool _isNew = true;
  bool _isSaving = false;
  bool _isFinalRate = true;

  final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>();
  final ScrollController _scrollController = ScrollController();

  TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
  TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0);

  final _startTimeController = TextEditingController(text: '');
  final _endTimeController = TextEditingController(text: '');
  final _unitsController = TextEditingController(text: Utils.toStringMatchingTemplateFractionPrecision(0, Settings.insulinSteps));
  final _carbsController = TextEditingController(text: Utils.toStringMatchingTemplateFractionPrecision(0, Settings.nutritionSteps));
  final _mgPerDlController = TextEditingController(text: '0');
  final _mmolPerLController = TextEditingController(text: Utils.toStringMatchingTemplateFractionPrecision(0, Settings.mmolPerLSteps));

  @override
  void initState() {
    super.initState();
    reload();

    if (widget.suggestedStartTime != null) {
      _startTime = widget.suggestedStartTime!;
    }
    if (widget.suggestedEndTime != null) {
      _endTime = widget.suggestedEndTime!;
    }

    if (_bolus != null) {
      _startTime = TimeOfDay.fromDateTime(_bolus!.startTime);

      _endTime = TimeOfDay.fromDateTime(_bolus!.endTime);

      _unitsController.text = _bolus!.units.toString();
      _carbsController.text = _bolus!.carbs.toString();
      _mgPerDlController.text = (_bolus!.mgPerDl ?? '').toString();
      _mmolPerLController.text = (_bolus!.mmolPerL ?? '').toString();
    }
    _startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
    _endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
  }

  @override
  void dispose() {
    _scrollController.dispose();
    _startTimeController.dispose();
    _endTimeController.dispose();
    _unitsController.dispose();
    _carbsController.dispose();
    _mgPerDlController.dispose();
    _mmolPerLController.dispose();
    super.dispose();
  }

  void reload({String? message}) {
    if (widget.id != 0) {
      setState(() {
        _bolus = Bolus.get(widget.id);
      });
    }
    _isNew = _bolus == null;

    setState(() {
      if (message != null) {
        var snackBar = SnackBar(
          content: Text(message),
          duration: const Duration(seconds: 2),
        );
        ScaffoldMessenger.of(context)
          ..removeCurrentSnackBar()
          ..showSnackBar(snackBar);
      }
    });
  }

  void updateStartTime(TimeOfDay? value) {
    if (value != null) {
      setState(() {
        _startTime = value;
        _startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
      });
    }
  }

  void updateEndTime(TimeOfDay? value) {
    if (value != null) {
      setState(() {
        _endTime = value;
        _endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
        _isFinalRate = widget.suggestedEndTime == null ||
            _endTime == widget.suggestedEndTime!;
      });
    }
  }

  Future<String?> validateTimePeriod() async {
    String? error;
    List<Bolus> bolusRates = Bolus.getAllForProfile(widget.bolusProfileId);

    // check for duplicates
    if (bolusRates
        .where((other) =>
            widget.id != other.id &&
            _startTime.hour == other.startTime.hour &&
            _startTime.minute == other.startTime.minute)
        .isNotEmpty) {
      error = 'There\'s already a rate with this start time.';
    }

    if (bolusRates
        .where((other) =>
            (widget.id != other.id) &&
            DateTimeUtils.convertTimeOfDayToDateTime(_startTime)
                .isBefore(other.startTime) &&
            DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
                .isAfter(other.startTime))
        .isNotEmpty) {
      error = 'This rate\'s time period overlaps with another one.';
    }

    return error == null
        ? null
        : showDialog(
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                content: Text(error!),
                actions: <Widget>[
                  TextButton(
                    onPressed: () => Navigator.pop(context, 'CANCEL'),
                    child: const Text('GO BACK TO EDITING'),
                  ),
                  ElevatedButton(
                    onPressed: () => Navigator.pop(context, 'CONFIRM'),
                    child: const Text('SAVE AS IS'),
                  ),
                ],
              );
            });
  }

  void handleSaveAction({bool next = true}) async {
    setState(() {
      _isSaving = true;
    });

    if (_bolusForm.currentState!.validate()) {
      await validateTimePeriod().then((value) async {
        if (value != 'CANCEL') {
          Bolus bolus = Bolus(
            id: widget.id,
            startTime: DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
            endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime),
            units: double.tryParse(_unitsController.text) ?? 0,
            carbs: double.tryParse(_carbsController.text) ?? 0,
            mgPerDl: int.tryParse(_mgPerDlController.text),
            mmolPerL: double.tryParse(_mmolPerLController.text),
          );
          bolus.bolusProfile.targetId = widget.bolusProfileId;
          Bolus.put(bolus);

          if (next) {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) {
                  return BolusDetailScreen(
                    bolusProfileId: widget.bolusProfileId,
                    suggestedStartTime: _endTime,
                    suggestedEndTime: widget.suggestedEndTime,
                  );
                },
              ),
            ).then((result) {
              Navigator.pop(
                context,
                ['New Bolus Rate${result[1] != null ? 's' : ''} saved', bolus] +
                    [result[1]],
              );
            });
          } else {
            Navigator.pop(
                context, ['${_isNew ? 'New' : ''} Bolus Rate saved', bolus]);
          }
        }
      });
    }

    setState(() {
      _isSaving = false;
    });
  }

  void handleCancelAction() {
    if (Settings.get().showConfirmationDialogOnCancel &&
        ((_isNew &&
                (_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
                    _endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||
                    _startTime.minute !=
                        (widget.suggestedStartTime?.minute ?? 0) ||
                    _endTime.minute != (widget.suggestedEndTime?.minute ?? 0) ||
                    (double.tryParse(_unitsController.text) ?? 0) != 0.0 ||
                    (double.tryParse(_carbsController.text) ?? 0) != 0.0 ||
                    (int.tryParse(_mgPerDlController.text) ?? 0) != 0 ||
                    (double.tryParse(_mmolPerLController.text) ?? 0) != 0.0)) ||
            (!_isNew &&
                (TimeOfDay.fromDateTime(_bolus!.startTime) != _startTime ||
                    TimeOfDay.fromDateTime(_bolus!.endTime) != _endTime ||
                    (double.tryParse(_unitsController.text) ?? 0) !=
                        _bolus!.units ||
                    (double.tryParse(_carbsController.text) ?? 0) !=
                        _bolus!.carbs ||
                    (double.tryParse(_mgPerDlController.text) ?? 0) !=
                        _bolus!.mgPerDl ||
                    (double.tryParse(_mmolPerLController.text) ?? 0) !=
                        _bolus!.mmolPerL)))) {
      DialogUtils.showCancelConfirmationDialog(
        context: context,
        isNew: _isNew,
        onSave: handleSaveAction,
      );
    } else {
      Navigator.pop(context);
    }
  }

  void convertBetweenMgPerDlAndMmolPerL(double? value) async {
    if (value != null) {
      if (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl &&
          _mgPerDlController.text != '') {
        _mgPerDlController.text = value.toInt().toString();
        setState(() {
          _mmolPerLController.text =
              Utils.convertMgPerDlToMmolPerL(value.toInt()).toString();
        });
      }
      if (Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL &&
          _mmolPerLController.text != '') {
        _mmolPerLController.text =
            Utils.toStringMatchingTemplateFractionPrecision(
                value, Settings.mmolPerLSteps);
        setState(() {
          _mgPerDlController.text =
              Utils.convertMmolPerLToMgPerDl(value.toDouble()).toString();
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
            '${_isNew ? 'New' : 'Edit'} Bolus Rate for ${BolusProfile.get(widget.bolusProfileId)?.name}'),
      ),
      drawer: const Navigation(currentLocation: BolusDetailScreen.routeName),
      body: Scrollbar(
        controller: _scrollController,
        child: SingleChildScrollView(
          controller: _scrollController,
          child: Column(
            children: [
              FormWrapper(
                formState: _bolusForm,
                fields: [
                  Row(
                    children: [
                      Expanded(
                        child: Padding(
                          padding: const EdgeInsets.only(right: 5),
                          child: TimeOfDayFormField(
                            label: 'Start Time',
                            controller: _startTimeController,
                            time: _startTime,
                            onChanged: updateStartTime,
                          ),
                        ),
                      ),
                      Expanded(
                        child: Padding(
                          padding: const EdgeInsets.only(left: 5),
                          child: TimeOfDayFormField(
                            label: 'End Time',
                            controller: _endTimeController,
                            time: _endTime,
                            onChanged: updateEndTime,
                          ),
                        ),
                      ),
                    ],
                  ),
                  NumberFormField(
                    controller: _unitsController,
                    label: 'Units',
                    suffix: 'U',
                    autoRoundToMultipleOfStep: true,
                    step: Settings.insulinSteps,
                    onChanged: (value) {
                      if (value != null) {
                        _unitsController.text =
                            Utils.toStringMatchingTemplateFractionPrecision(
                                value, Settings.insulinSteps);
                      }
                    },
                  ),
                  NumberFormField(
                    controller: _carbsController,
                    label: 'per carbs',
                    suffix: Settings.nutritionMeasurementSuffix,
                    autoRoundToMultipleOfStep: true,
                    step: Settings.nutritionSteps,
                    onChanged: (value) {
                      if (value != null) {
                        _carbsController.text =
                            Utils.toStringMatchingTemplateFractionPrecision(
                                value, Settings.nutritionSteps);
                      }
                    },
                  ),
                  Row(
                    children: [
                      Settings.glucoseMeasurement ==
                                  GlucoseMeasurement.mgPerDl ||
                              Settings.glucoseDisplayMode ==
                                  GlucoseDisplayMode.both ||
                              Settings.glucoseDisplayMode ==
                                  GlucoseDisplayMode.bothForDetail
                          ? Expanded(
                            flex: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 2 : 1,
                              child: NumberFormField(
                                label: 'per mg/dl',
                                suffix: 'mg/dl',
                                readOnly: Settings.glucoseMeasurement ==
                                    GlucoseMeasurement.mmolPerL,
                                showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl,
                                controller: _mgPerDlController,
                                onChanged: convertBetweenMgPerDlAndMmolPerL,
                              ),
                            )
                          : Container(),
                      Settings.glucoseMeasurement ==
                                  GlucoseMeasurement.mmolPerL ||
                              [
                                GlucoseDisplayMode.both,
                                GlucoseDisplayMode.bothForDetail
                              ].contains(Settings.glucoseDisplayMode)
                          ? Expanded(
                            flex: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ? 2 : 1,
                              child: NumberFormField(
                                label: 'per mmol/l',
                                suffix: 'mmol/l',
                                readOnly: Settings.glucoseMeasurement ==
                                    GlucoseMeasurement.mgPerDl,
                                showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL,
                                controller: _mmolPerLController,
                                step: Settings.mmolPerLSteps,
                                onChanged: convertBetweenMgPerDlAndMmolPerL,
                              ),
                            )
                          : Container(),
                    ],
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
      bottomNavigationBar: DetailBottomRow(
        onCancel: handleCancelAction,
        onAction:
            _isSaving ? null : () => handleSaveAction(next: !_isFinalRate),
        onMiddleAction: _isSaving || _isFinalRate
            ? null
            : () => handleSaveAction(next: false),
        actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT',
        middleActionText: 'SAVE & CLOSE',
      ),
    );
  }
}
