import 'package:diameter/main.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Bolus_, BolusProfile_;

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

  // properties
  int id;
  bool deleted;
  @Property(type: PropertyType.date)
  DateTime startTime;
  @Property(type: PropertyType.date)
  DateTime endTime;
  double units;
  double carbs;
  int? mgPerDl;
  double? mmolPerL;
  String? source;

  // relations
  final bolusProfile = ToOne<BolusProfile>();

  // constructor
  Bolus({
    this.id = 0,
    this.deleted = false,
    required this.startTime,
    required this.endTime,
    this.units = 0,
    this.carbs = 0,
    this.mgPerDl,
    this.mmolPerL,
    this.source,
  });

  // methods
  static Bolus? get(int id) => box.get(id);
  static void put(Bolus bolus) => box.put(bolus);

  static List<Bolus> getAllForProfile(int id) {
    QueryBuilder<Bolus> builder = box.query(Bolus_.deleted.equals(false))
      ..order(Bolus_.startTime);
    builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
    return builder.build().find();
  }

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

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

  static Bolus? getRateForTime(DateTime? dateTime) {
    if (dateTime != null) {
      final bolusProfile = BolusProfile.getActive(dateTime);
      final time = DateTimeUtils.convertTimeOfDayToDateTime(
          TimeOfDay.fromDateTime(dateTime));
      if (bolusProfile != null) {
        final rates = Bolus.getAllForProfile(bolusProfile.id);
        final result = rates.where((rate) {
          DateTime endTime = rate.endTime == dummyDate
              ? rate.endTime.add(const Duration(days: 1))
              : rate.endTime;
          return (time.isAfter(rate.startTime) ||
                  time.isAtSameMomentAs(rate.startTime)) &&
              time.isBefore(endTime);
        });
        return result.length != 1 ? null : result.single;
      }
    }
    return null;
  }

  @override
  String toString() {
    return DateTimeUtils.displayTime(startTime);
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['deleted'] = deleted;
    data['startTime'] = startTime.toIso8601String();
    data['endTime'] = endTime.toIso8601String();
    data['units'] = units;
    data['carbs'] = carbs;
    data['mgPerDl'] = mgPerDl;
    data['mmolPerL'] = mmolPerL;
    data['bolusProfile'] = bolusProfile.targetId;
    return data;
  }

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

    if (startTime == null || endTime == null) {
      return startTime == null
          ? endTime == null
              ? 'start and end time are missing'
              : 'start time is missing'
          : 'end time is missing';
    }

    final bolus = Bolus(
      id: overrideExisting ? json['id'] : 0,
      deleted: json['deleted'],
      startTime: startTime,
      endTime: endTime,
      units: json['units'],
      carbs: json['carbs'],
      mgPerDl: json['mgPerDl'],
      mmolPerL: json['mmolPerL'],
      source: source,
    );

    bolus.bolusProfile.targetId = json['bolusProfile'];
    Bolus.put(bolus);

    return null;
  }
}
