import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show LogEntry_;

@Entity(uid: 752131069307970560)
@Sync()
class LogEntry {
  static final Box<LogEntry> box = objectBox.store.box<LogEntry>();

  // properties
  int id;
  bool deleted;
  @Property(type: PropertyType.date)
  DateTime time;
  int? mgPerDl;
  double? mmolPerL;
  double? glucoseTrend;
  String? notes;
  String? source;

  // constructor
  LogEntry({
    this.id = 0,
    this.deleted = false,
    required this.time,
    this.mgPerDl,
    this.mmolPerL,
    this.glucoseTrend,
    this.notes,
    this.source,
  });

  // methods
  static LogEntry? get(int id) => id == 0 ? null : box.get(id);
  static List<LogEntry> getAll() => box.getAll();
  static void put(LogEntry logEntry) => box.put(logEntry);

  static void remove(int id) {
    final item = box.get(id);
    if (item != null) {
      item.deleted = true;
      LogMeal.removeAllForEntry(id);
      LogBolus.removeAllForEntry(id);
      box.put(item);
    }
  }

  static bool hasUncorrectedGlucose(int id) {
    final entry = box.get(id);
    if (((entry?.mgPerDl ?? 0) > Settings.targetMgPerDl ||
        (entry?.mmolPerL ?? 0) > Settings.targetMmolPerL)) {
      return !LogBolus.glucoseBolusForEntryExists(id);
    }
    return false;
  }

  static List<LogEntry> getAllForDate(DateTime date) {
    DateTime startOfDay = DateTime(date.year, date.month, date.day);
    DateTime endOfDay = startOfDay.add(const Duration(days: 1));
    QueryBuilder<LogEntry> builder = box.query(LogEntry_.deleted.equals(false))
      ..order(LogEntry_.time, flags: Order.descending);
    return builder.build().find().where((entry) {
      return (entry.time.compareTo(startOfDay) >= 0 &&
          entry.time.isBefore(endOfDay));
    }).toList();
  }

  static List<LogEntry> getAllByFilter({
    DateTime? startDate,
    DateTime? endDate,
    int? minMgPerDl,
    int? maxMgPerDl,
    double? minMmolPerL,
    double? maxMmolPerL,
    int? mealId,
    String? mealName,
    String? note,
  }) {
    DateTime start = startDate ?? DateTime(2000, 1, 1);
    DateTime end = endDate ?? DateTime.now();
    QueryBuilder<LogEntry> builder = box.query(LogEntry_.deleted.equals(false) &
        (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
            ? LogEntry_.mgPerDl.between(
                minMgPerDl ?? 0, maxMgPerDl ?? double.maxFinite.toInt())
            : LogEntry_.mmolPerL
                .between(minMmolPerL ?? 0, maxMmolPerL ?? double.maxFinite)))
      ..order(LogEntry_.time, flags: Order.descending);

    return builder.build().find().where((entry) {
      return (note == null || (entry.notes ?? '').contains(note)) &&
          (mealId == null ||
              LogMeal.getAllForEntry(entry.id)
                  .any((LogMeal logMeal) => logMeal.meal.targetId == mealId)) &&
          (mealName == null ||
              LogMeal.getAllForEntry(entry.id).any(
                  (LogMeal logMeal) => logMeal.value.contains(mealName))) &&
          (entry.time.compareTo(start) >= 0 && entry.time.isBefore(end));
    }).toList();
  }

  @override
  String toString() {
    return DateTimeUtils.displayDateTime(time);
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['deleted'] = deleted;
    data['time'] = time.toIso8601String();
    data['mgPerDl'] = mgPerDl;
    data['mmolPerL'] = mmolPerL;
    data['glucoseTrend'] = glucoseTrend;
    data['notes'] = notes;
    return data;
  }

  static String? putFromJson(
      Map<String, dynamic> json, bool overrideExisting, String? source) {
    DateTime? time = DateTime.tryParse(json['time']);

    if (time == null) {
      return 'time is missing';
    }

    final logEntry = LogEntry(
      id: overrideExisting ? json['id'] : 0,
      deleted: json['deleted'],
      time: time,
      mgPerDl: json['mgPerDl'],
      mmolPerL: json['mmolPerL'],
      glucoseTrend: json['glucoseTrend'],
      notes: json['notes'],
      source: source,
    );

    LogEntry.put(logEntry);

    return null;
  }
}
