// Flutter
import 'package:flutter/material.dart';
import 'package:flutter_expense_tracker/models/asset.dart';
import 'package:flutter_expense_tracker/models/income.dart';

// Local
import '/models/tracked_item.dart';
import '/models/item_type.dart';
import '/models/expense.dart';
import '/models/frequency.dart';
import '/db.dart';

class TrackedItemPage extends StatefulWidget {
  final Future<List<TrackedItem>> assetsToLoad;

  const TrackedItemPage({
    super.key,
    required this.assetsToLoad,
  });

  @override
  State<TrackedItemPage> createState() => _TrackedItemPageState();
}

class _TrackedItemPageState extends State<TrackedItemPage> {
  refresh() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return FutureBuilder<List<TrackedItem>>(
        future: widget.assetsToLoad,
        builder:
            (BuildContext context, AsyncSnapshot<List<TrackedItem>> snapshot) {
          if (!snapshot.hasData) {
            return Text('Loading...');
          }
          snapshot.data!.sort(
            (a, b) => (b.calcComparableAmountYearly() -
                    a.calcComparableAmountYearly())
                .toInt(),
          );
          return snapshot.data!.isEmpty
              ? Text(
                  "Add items to get started.",
                  softWrap: true,
                )
              : ListView.builder(
                  itemCount: snapshot.data!.length,
                  itemBuilder: (_, index) {
                    final TrackedItem curr = snapshot.data![index];

                    final itemKey = Key(curr.id!.toString());
                    final String itemTitle = curr.name;

                    final String itemAmount;
                    if (curr.frequency != null) {
                      itemAmount =
                          "${curr.amount.toStringAsFixed(2)} ${curr.frequency!.title}";
                    } else {
                      itemAmount = curr.amount.toStringAsFixed(2);
                    }
                    final String itemDescription = curr.description;

                    final double itemDayAmount, itemMonthAmount, itemYearAmount;
                    final String estimateSymbolDaily,
                        estimateSymbolMonthly,
                        estimateSymbolYearly;

                    if (curr.frequency != null) {
                      itemDayAmount = curr.calcComparableAmountDaily();
                      estimateSymbolDaily = curr.frequency!.numDays
                                  .toStringAsFixed(2)
                                  .endsWith(".00") &&
                              itemDayAmount.toStringAsFixed(3).endsWith("0")
                          ? ""
                          : "~";

                      itemMonthAmount =
                          (curr.calcComparableAmountYearly() / 12);
                      estimateSymbolMonthly = curr.frequency!.timesPerYear
                                  .toStringAsFixed(2)
                                  .endsWith(".00") &&
                              itemMonthAmount.toStringAsFixed(3).endsWith("0")
                          ? ""
                          : "~";

                      itemYearAmount = curr.calcComparableAmountYearly();
                      estimateSymbolYearly = curr.frequency!.timesPerYear
                                  .toStringAsFixed(2)
                                  .endsWith(".00") &&
                              itemYearAmount.toStringAsFixed(3).endsWith("0")
                          ? ""
                          : "~";
                    } else {
                      itemDayAmount = -1;
                      estimateSymbolDaily = "";
                      itemMonthAmount = curr.amount;
                      estimateSymbolMonthly = "";
                      itemYearAmount = -1;
                      estimateSymbolYearly = "";
                    }
                    final String itemTopText = itemDayAmount < 0
                        ? ""
                        : "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}";
                    final String itemMiddleText = itemMonthAmount < 0
                        ? ""
                        : "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)} ${Frequency.monthly.title}";
                    final String itemBottomText = itemYearAmount < 0
                        ? ""
                        : "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}";

                    return Padding(
                      padding: const EdgeInsets.all(4.0),
                      child: Dismissible(
                        key: itemKey,
                        background: Container(
                          color: Colors.red,
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.start,
                            children: [
                              Icon(Icons.delete),
                              Text("Delete"),
                            ],
                          ),
                        ),
                        secondaryBackground: Container(
                          color: Colors.orange,
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.end,
                            children: [
                              Text("Edit"),
                              Icon(Icons.edit),
                            ],
                          ),
                        ),
                        onDismissed: (direction) {
                          setState(() {
                            snapshot.data!.remove(curr);
                            switch (direction) {
                              case DismissDirection.startToEnd:
                                // Remove the item from the database.
                                if (curr is Expense) {
                                  DatabaseHelper.instance.removeExpense(
                                    curr.id!,
                                  );
                                } else if (curr is Income) {
                                  DatabaseHelper.instance.removeIncome(
                                    curr.id!,
                                  );
                                } else if (curr is Asset) {
                                  DatabaseHelper.instance.removeAsset(
                                    curr.id!,
                                  );
                                } else {
                                  throw UnimplementedError(
                                      "Cannot remove unimplemented item type.");
                                }
                                break;
                              case DismissDirection.endToStart:
                                // Open an edit dialog, then remove the item from the list.
                                showDialog(
                                  context: context,
                                  builder: (_) => AlertDialog(
                                    content: TrackedItemInputDialog(
                                      notifyParent: refresh,
                                      entry: curr,
                                      amountText: curr.getAmountText(),
                                      type: curr.type!,
                                    ),
                                  ),
                                );
                                break;
                              default:
                                UnimplementedError(
                                  "Direction ${direction.toString()} not recognized.",
                                );
                            }
                          });
                        },
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(4),
                            color: theme.colorScheme.onPrimary,
                          ),
                          child: Padding(
                            padding: const EdgeInsets.all(4.0),
                            child: Row(
                              mainAxisSize: MainAxisSize.max,
                              children: [
                                Column(
                                  mainAxisSize: MainAxisSize.min,
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Text(
                                      itemTitle,
                                      style: TextStyle(fontSize: 20.0),
                                    ),
                                    Text(
                                      itemAmount,
                                      style: TextStyle(fontSize: 12.0),
                                    ),
                                  ],
                                ),
                                Expanded(
                                  child: Center(
                                    child: Text(
                                      itemDescription,
                                      style: TextStyle(
                                        fontSize: 12.0,
                                      ),
                                      softWrap: true,
                                      textAlign: TextAlign.center,
                                    ),
                                  ),
                                ),
                                Column(
                                  crossAxisAlignment: CrossAxisAlignment.end,
                                  children: [
                                    Text(
                                      itemTopText,
                                      style: TextStyle(fontSize: 12.0),
                                    ),
                                    Text(
                                      itemMiddleText,
                                      style: TextStyle(fontSize: 12.0),
                                    ),
                                    Text(
                                      itemBottomText,
                                      style: TextStyle(fontSize: 12.0),
                                    ),
                                  ],
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                    );
                  },
                );
        });
  }
}

class TrackedItemInputDialog extends StatefulWidget {
  final Function() notifyParent;
  final TrackedItem? entry;
  final String? amountText;
  final ItemType? type;

  const TrackedItemInputDialog({
    super.key,
    required this.notifyParent,
    this.entry,
    this.amountText,
    this.type,
  });

  @override
  State<TrackedItemInputDialog> createState() => _TrackedItemInputDialogState();
}

class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> {
  final _formKey = GlobalKey<FormState>();

  int? _id;
  String _name = "";
  double _amount = 0;
  Frequency _freq = Frequency.monthly;
  String _desc = "";

  @override
  Widget build(BuildContext context) {
    if (widget.type == null &&
        (widget.entry != null && widget.entry!.type == null)) {
      throw FlutterError("No ItemType provided for TrackedItemInputDialog.");
    }
    ItemType? _type = widget.type;

    if (widget.entry != null) {
      _id = widget.entry!.id;
      _name = widget.entry!.name;
      _amount = widget.entry!.amount;
      widget.entry!.frequency == null ? null : _freq = widget.entry!.frequency!;
      _desc = widget.entry!.description;
      _type = widget.entry!.type!;
    }

    String amountText =
        widget.amountText != null ? widget.amountText! : TrackedItem.amountText;

    return Column(
      // prevent AlertDialog from taking full vertical height.
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          alignment: FractionalOffset.topRight,
          child: IconButton(
            onPressed: () {
              if (widget.entry != null) {
                setState(() {
                  switch (_type) {
                    case ItemType.expense:
                      DatabaseHelper.instance.addExpense(widget.entry!);
                      break;
                    case ItemType.income:
                      DatabaseHelper.instance.addIncome(widget.entry!);
                      break;
                    case ItemType.asset:
                      DatabaseHelper.instance.addAsset(widget.entry!);
                      break;
                    default:
                      throw UnimplementedError(
                          "Cannot add unimplemented type.");
                  }
                  widget.notifyParent();
                });
              }
              Navigator.of(context).pop();
            },
            icon: Icon(Icons.clear),
          ),
        ),
        AlertDialog(
          insetPadding: EdgeInsets.all(0),
          title: Center(
            child: widget.entry == null
                ? Text("New ${_type!.title}")
                : Text("Edit Expense"),
          ),
          content: FutureBuilder<List<Expense>>(
              future: DatabaseHelper.instance.getExpenses(),
              builder: (BuildContext context,
                  AsyncSnapshot<List<Expense>> snapshot) {
                if (!snapshot.hasData) {
                  return Center(child: Text('Loading...'));
                }
                List<Expense> expenses = snapshot.data!;
                return Form(
                  key: _formKey,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      TextFormField(
                        keyboardType: TextInputType.text,
                        textCapitalization: TextCapitalization.words,
                        decoration: InputDecoration(
                          labelText: "Name",
                          hintText: "Example: Red Pocket",
                          hintStyle: TextStyle(fontSize: 10.0),
                          errorStyle: TextStyle(fontSize: 10.0),
                        ),
                        initialValue: _name,
                        validator: (value) {
                          if (value!.isEmpty) {
                            return "Name must be provided.";
                          }
                          if (!expenses.every((expense) =>
                              expense.name != value || expense.id == _id)) {
                            return "Name must be unique, already in use.";
                          }
                          return null;
                        },
                        onSaved: (value) {
                          _name = value!;
                        },
                      ),
                      TextFormField(
                        keyboardType:
                            TextInputType.numberWithOptions(decimal: true),
                        decoration: InputDecoration(
                          labelText: amountText,
                          hintText: "Example: 10.00",
                          hintStyle: TextStyle(fontSize: 10.0),
                          errorStyle: TextStyle(fontSize: 10.0),
                        ),
                        initialValue: _amount != 0 ? _amount.toString() : "",
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return "$amountText must be provided.";
                          }
                          if (double.tryParse(value) == null) {
                            return "$amountText must be a valid number.";
                          }
                          if (double.parse(value) < 0) {
                            return "Please use the Income page rather than having negative expenses.";
                          }
                          if (double.parse(value) < 0.01) {
                            return "$amountText must be one hundreth (0.01) or higher.";
                          }
                          return null;
                        },
                        onSaved: (value) {
                          _amount = double.parse(value!);
                        },
                      ),
                      DropdownButtonFormField(
                        items: Frequency.values
                            .map(
                              (freq) => DropdownMenuItem(
                                value: freq,
                                child: Row(
                                  children: [
                                    Text(
                                      freq.title,
                                    ),
                                    Padding(
                                      padding: EdgeInsets.all(1.0),
                                      child: Text(
                                        " (${freq.hint})",
                                        style: TextStyle(fontSize: 10.0),
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            )
                            .toList(),
                        value: _freq,
                        decoration: InputDecoration(
                          labelText: "Frequency",
                          errorStyle: TextStyle(fontSize: 10.0),
                        ),
                        validator: (value) {
                          if (value == null) {
                            return "Frequency must be provided.";
                          }
                          if (!Frequency.values.contains(value)) {
                            return "Value not valid.";
                          }
                          return null;
                        },
                        onChanged: (value) {
                          _freq = value!;
                        },
                      ),
                      TextFormField(
                        keyboardType: TextInputType.text,
                        textCapitalization: TextCapitalization.sentences,
                        decoration: InputDecoration(
                          labelText: "Description",
                          hintText:
                              "Example: 1GB data with unlimited talk & text.",
                          hintStyle: TextStyle(fontSize: 8.0),
                          errorStyle: TextStyle(fontSize: 10.0),
                        ),
                        initialValue: _desc,
                        validator: (value) {
                          return null;
                        },
                        onSaved: (value) {
                          _desc = value!;
                        },
                      ),
                    ],
                  ),
                );
              }),
          actions: [
            Center(
              child: ElevatedButton.icon(
                onPressed: () {
                  if (_formKey.currentState!.validate()) {
                    _formKey.currentState!.save();
                    setState(() {
                      Expense expense = Expense(
                        id: _id,
                        name: _name,
                        amount: _amount,
                        frequency: _freq,
                        description: _desc,
                      );
                      if (_id != null) {
                        DatabaseHelper.instance.updateExpense(
                          expense,
                        );
                      } else {
                        DatabaseHelper.instance.addExpense(
                          expense,
                        );
                      }
                    });
                    widget.notifyParent();
                    Navigator.of(context).pop();
                  }
                },
                icon: Icon(Icons.save),
                label: Text('Submit'),
              ),
            )
          ],
        ),
      ],
    );
  }
}