import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show LogMeal_, LogEntry_;

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

  // properties
  int id;
  bool deleted;
  String value;
  double? carbsRatio;
  double? portionSize;
  double? totalCarbs;
  String? notes;
  double amount;
  String? source;

  // relations
  final logEntry = ToOne<LogEntry>();
  final meal = ToOne<Meal>();
  final mealSource = ToOne<MealSource>();
  final mealCategory = ToOne<MealCategory>();
  final mealPortionType = ToOne<MealPortionType>();
  final portionSizeAccuracy = ToOne<Accuracy>();
  final carbsRatioAccuracy = ToOne<Accuracy>();

  // constructor
  LogMeal({
    this.id = 0,
    this.deleted = false,
    this.value = '',
    this.amount = 1,
    this.carbsRatio,
    this.portionSize,
    this.totalCarbs,
    this.notes,
    this.source,
  });

  // methods
  static LogMeal? get(int id) => box.get(id);
  static void put(LogMeal logMeal) => box.put(logMeal);
  static void remove(int id) {
    final item = box.get(id);
    if (item != null) {
      item.deleted = true;
      box.put(item);
    }
  }

  static void removeAllForEntry(int id) {
    box.putMany(getAllForEntry(id).map((item) {
      item.deleted = true;
      return item;
    }).toList());
  }

  static List<LogMeal> getAllForEntry(int id) {
    QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
    builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
    return builder.build().find();
  }

  static List<LogMeal> getRecentWithoutBolus(int id) {
    final dateTime = LogEntry.get(id)?.time ?? DateTime.now();
    QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
    builder.link(LogMeal_.logEntry);
    List<LogMeal> results = builder.build().find();
    results.retainWhere((logMeal) {
      final entryTime = logMeal.logEntry.target!.time;
      return entryTime.isAfter(dateTime.subtract(const Duration(hours: 12))) &&
          entryTime.isBefore(dateTime.add(const Duration(hours: 12))) && !LogBolus.bolusForMealExists(logMeal.id);
    });
    return results;
  }

  static double getTotalCarbsForEntry(int id) {
    QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
    builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
    return builder.build().property(LogMeal_.totalCarbs).sum();
  }

  @override
  String toString() {
    return value;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['deleted'] = deleted;
    data['value'] = value;
    data['carbsRatio'] = carbsRatio;
    data['portionSize'] = portionSize;
    data['totalCarbs'] = totalCarbs;
    data['notes'] = notes;
    data['amount'] = amount;
    data['logEntry'] = logEntry.targetId;
    data['meal'] = meal.targetId;
    data['mealSource'] = mealSource.targetId;
    data['mealCategory'] = mealCategory.targetId;
    data['mealPortionType'] = mealPortionType.targetId;
    data['portionSizeAccuracy'] = portionSizeAccuracy.targetId;
    data['carbsRatioAccuracy'] = carbsRatioAccuracy.targetId;
    return data;
  }

  static String? putFromJson(
      Map<String, dynamic> json, bool overrideExisting, String? source) {
    final logMeal = LogMeal(
      id: overrideExisting ? json['id'] : 0,
      deleted: json['deleted'] == 'true',
      value: json['value'],
      carbsRatio: json['carbsRatio'],
      portionSize: json['portionSize'],
      totalCarbs: json['totalCarbs'],
      amount: json['amount'],
      notes: json['notes'], 
      source: source,
    );

    logMeal.logEntry.targetId = json['logEntry'];
    logMeal.meal.targetId = json['meal'];
    logMeal.mealSource.targetId = json['mealSource'];
    logMeal.mealCategory.targetId = json['mealCategory'];
    logMeal.mealPortionType.targetId = json['mealPortionType'];
    logMeal.portionSizeAccuracy.targetId = json['portionSizeAccuracy'];
    logMeal.carbsRatioAccuracy.targetId = json['carbsRatioAccuracy'];

    LogMeal.put(logMeal);
    return null;
  }
}
