import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/date_time_form_field.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/components/forms/form_wrapper.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
import 'package:diameter/screens/log/log_entry/log_bolus_list.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;

class LogEntryScreen extends StatefulWidget {
  static const String routeName = '/log-entry';
  final int id;
  final DateTime? suggestedDate;

  const LogEntryScreen({Key? key, this.id = 0, this.suggestedDate}) : super(key: key);

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

class _LogEntryScreenState extends State<LogEntryScreen> {
  LogEntry? _logEntry;
  List<LogMeal> _logMeals = [];
  List<LogBolus> _logBoli = [];

  bool _isNew = true;

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

  late DateTime _time;
  double? _glucoseTrend;

  final _timeController = TextEditingController(text: '');
  final _dateController = TextEditingController(text: '');
  final _mgPerDlController = TextEditingController(text: '');
  final _mmolPerLController = TextEditingController(text: '');
  final _notesController = TextEditingController(text: '');

  late FloatingActionButton addMealButton;
  late FloatingActionButton addBolusButton;
  late IconButton refreshButton;
  late IconButton closeButton;
  late DetailBottomRow detailBottomRow;
  late DetailBottomRow detailBottomRowWhileSaving;

  FloatingActionButton? actionButton;
  List<Widget> appBarActions = [];
  DetailBottomRow? bottomNav;

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

    reload();

    addMealButton = FloatingActionButton(
      onPressed: handleAddNewMeal,
      child: const Icon(Icons.add),
    );

    addBolusButton = FloatingActionButton(
      onPressed: handleAddNewBolus,
      child: const Icon(Icons.add),
    );

    refreshButton = IconButton(
      icon: const Icon(Icons.refresh),
      onPressed: reload,
    );

    closeButton = IconButton(
      onPressed: handleCancelAction,
      icon: const Icon(Icons.close),
    );

    detailBottomRow = DetailBottomRow(
      onCancel: handleCancelAction,
      onAction: handleSaveAction,
      onMiddleAction: () => handleSaveAction(close: true),
    );

    detailBottomRowWhileSaving = DetailBottomRow(
      onCancel: handleCancelAction,
      onAction: null,
    );

    actionButton = null;
    appBarActions = [closeButton];
    bottomNav = detailBottomRow;

    if (_logEntry != null) {
      _time = _logEntry!.time;
      _mgPerDlController.text = (_logEntry!.mgPerDl ?? '').toString();
      _mmolPerLController.text = (_logEntry!.mmolPerL ?? '').toString();
      _glucoseTrend = _logEntry!.glucoseTrend;
      _notesController.text = _logEntry!.notes ?? '';
    } else {
      _time = widget.suggestedDate ?? DateTime.now();
    }

    updateTime();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    _timeController.dispose();
    _dateController.dispose();
    _mgPerDlController.dispose();
    _mmolPerLController.dispose();
    _notesController.dispose();
    super.dispose();
  }

  void reload({String? message}) {
    if (widget.id != 0) {
      setState(() {
        _logEntry = LogEntry.get(widget.id);
        _logMeals = LogMeal.getAllForEntry(widget.id);
        _logBoli = LogBolus.getAllForEntry(widget.id);
      });
      _isNew = _logEntry == null;
    }

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

  void updateTime() {
    _timeController.text = DateTimeUtils.displayTime(_time);
    _dateController.text = DateTimeUtils.displayDate(_time);
  }

  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();
        });
      }
    }
  }

  void handleSaveAction({bool close = false}) async {
    setState(() {
      bottomNav = detailBottomRowWhileSaving;
    });
    if (logEntryForm.currentState!.validate()) {
      LogEntry logEntry = LogEntry(
        id: widget.id,
        time: _time,
        mgPerDl: int.tryParse(_mgPerDlController.text),
        mmolPerL: double.tryParse(_mmolPerLController.text),
        glucoseTrend: _glucoseTrend,
        notes: _notesController.text,
      );
      LogEntry.put(logEntry);

      if (close) {
        Navigator.pop(
            context, ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]);
      } else {
        if (_isNew) {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => LogEntryScreen(id: logEntry.id),
            ),
          ).then((result) => Navigator.pop(context, result));
        } else {
          reload(message: 'Log Entry Saved');
        }
      }
    }

    setState(() {
      bottomNav = detailBottomRow;
    });
  }

  void handleCancelAction() {
    if (Settings.get().showConfirmationDialogOnCancel &&
        ((_isNew &&
                (int.tryParse(_mgPerDlController.text) != null ||
                    double.tryParse(_mmolPerLController.text) != null ||
                    _notesController.text != '')) ||
            (!_isNew &&
                (int.tryParse(_mgPerDlController.text) != _logEntry!.mgPerDl ||
                    double.tryParse(_mmolPerLController.text) !=
                        _logEntry!.mmolPerL ||
                    _notesController.text != (_logEntry!.notes ?? ''))))) {
      DialogUtils.showCancelConfirmationDialog(
        context: context,
        isNew: _isNew,
        onSave: handleSaveAction,
        onDiscard: (context) => Navigator.pop(context),
      );
    } else {
      Navigator.pop(context);
    }
  }

  void handleAddNewMeal() async {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return LogMealDetailScreen(logEntryId: _logEntry!.id);
        },
      ),
    ).then((result) => reload(message: result?[0]));
  }

  void handleAddNewBolus() async {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return LogBolusDetailScreen(logEntryId: _logEntry!.id);
        },
      ),
    ).then((result) => reload(message: result?[0]));
  }

  void renderTabButtons(index) {
    if (_logEntry != null) {
      setState(() {
        switch (index) {
          case 1:
            actionButton = addMealButton;
            appBarActions = [refreshButton, closeButton];
            bottomNav = null;
            break;
          case 2:
            actionButton = addBolusButton;
            appBarActions = [refreshButton, closeButton];
            bottomNav = null;
            break;
          default:
            actionButton = null;
            appBarActions = [closeButton];
            bottomNav = detailBottomRow;
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: _isNew ? 1 : 3,
      child: Builder(builder: (BuildContext context) {
        final TabController tabController = DefaultTabController.of(context)!;
        tabController.addListener(() {
          renderTabButtons(tabController.index);
        });
        List<Widget> tabs = [
          Scrollbar(
            controller: _scrollController,
            child: SingleChildScrollView(
              controller: _scrollController,
              child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: <Widget>[
                    FormWrapper(
                      formState: logEntryForm,
                      fields: [
                        Row(
                          children: [
                            Expanded(
                              child: Padding(
                                padding: const EdgeInsets.only(right: 5),
                                child: DateTimeFormField(
                                  date: _time,
                                  label: 'Date',
                                  controller: _dateController,
                                  onChanged: (newTime) {
                                    if (newTime != null) {
                                      setState(() {
                                        _time = DateTime(
                                            newTime.year,
                                            newTime.month,
                                            newTime.day,
                                            _time.hour,
                                            _time.minute);
                                      });
                                      updateTime();
                                    }
                                  },
                                ),
                              ),
                            ),
                            Expanded(
                              child: Padding(
                                padding: const EdgeInsets.only(left: 5),
                                child: TimeOfDayFormField(
                                  time: TimeOfDay.fromDateTime(_time),
                                  label: 'Time',
                                  controller: _timeController,
                                  onChanged: (newTime) {
                                    if (newTime != null) {
                                      setState(() {
                                        _time = DateTime(
                                            _time.year,
                                            _time.month,
                                            _time.day,
                                            newTime.hour,
                                            newTime.minute);
                                      });
                                      updateTime();
                                    }
                                  },
                                ),
                              ),
                            ),
                          ],
                        ),
                        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: 'Blood Glucose',
                                      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: 'Blood Glucose',
                                      suffix: 'mmol/l',
                                      readOnly: Settings.glucoseMeasurement ==
                                          GlucoseMeasurement.mgPerDl,
                                      showSteppers:
                                          Settings.glucoseMeasurement ==
                                              GlucoseMeasurement.mmolPerL,
                                      controller: _mmolPerLController,
                                      step: Settings.mmolPerLSteps,
                                      onChanged:
                                          convertBetweenMgPerDlAndMmolPerL,
                                    ),
                                  )
                                : Container(),
                            Transform.rotate(
                              angle: (_glucoseTrend ?? 90) * math.pi / 180,
                              child: IconButton(
                                onPressed: () => setState(() {
                                  _glucoseTrend = (_glucoseTrend ?? -45) + 45;
                                  if (_glucoseTrend! > 180) {
                                    _glucoseTrend = null;
                                  }
                                }),
                                icon: Icon(Icons.arrow_upward,
                                    color: _glucoseTrend != null
                                        ? Theme.of(context).iconTheme.color
                                        : Theme.of(context).disabledColor),
                              ),
                            ),
                          ],
                        ),
                        TextFormField(
                          controller: _notesController,
                          decoration: const InputDecoration(
                            labelText: 'Notes',
                          ),
                          keyboardType: TextInputType.multiline,
                          minLines: 2,
                          maxLines: 5,
                        ),
                      ],
                    ),
                  ]),
            ),
          ),
        ];

        if (!_isNew) {
          tabs.add(LogMealListScreen(
              logEntry: _logEntry!, logMeals: _logMeals, reload: reload));
          tabs.add(LogBolusListScreen(
              logEntry: _logEntry!, logBoli: _logBoli, reload: reload));
        }

        return Scaffold(
          appBar: AppBar(
            title: Text(_isNew ? 'New Log Entry' : 'Edit Log Entry'),
            bottom: _isNew
                ? PreferredSize(child: Container(), preferredSize: Size.zero)
                : const TabBar(
                    tabs: [
                      Tab(text: 'GENERAL'),
                      Tab(text: 'MEALS'),
                      Tab(text: 'BOLI'),
                    ],
                  ),
            actions: appBarActions,
          ),
          drawer: const Navigation(currentLocation: LogEntryScreen.routeName),
          body: TabBarView(
            children: tabs,
          ),
          bottomNavigationBar: bottomNav,
          floatingActionButton: actionButton,
          floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
        );
      }),
    );
  }
}
