import 'package:diameter/main.dart';
import 'package:diameter/models/basal_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 Basal_, BasalProfile_;

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

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

  // relations
  final basalProfile = ToOne<BasalProfile>();

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

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

  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 List<Basal> getAllForProfile(int id) {
    QueryBuilder<Basal> builder = box.query(Basal_.deleted.equals(false))
      ..order(Basal_.startTime);
    builder.link(Basal_.basalProfile, BasalProfile_.id.equals(id));
    return builder.build().find();
  }

  static double getDailyTotalForProfile(int id) {
    double sum = 0.0;

    QueryBuilder<Basal> builder = box.query(Basal_.deleted.equals(false));
    builder.link(Basal_.basalProfile, BasalProfile_.id.equals(id));

    List<Basal> basalRates = builder.build().find();
    for (Basal basal in basalRates) {
      double rateDuration =
          basal.endTime.difference(basal.startTime).inMinutes / 60;
      if (rateDuration < 0) {
        rateDuration += 24;
      }
      sum += basal.units * rateDuration;
    }

    return sum;
  }

  static Basal? getRateForTime(DateTime? dateTime) {
    if (dateTime != null) {
      final basalProfile = BasalProfile.getActive(dateTime);
      final time = DateTimeUtils.convertTimeOfDayToDateTime(
          TimeOfDay.fromDateTime(dateTime));
      if (basalProfile != null) {
        final rates = Basal.getAllForProfile(basalProfile.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['basalProfile'] = basalProfile.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 basal = Basal(
      id: overrideExisting ? json['id'] : 0,
      deleted: json['deleted'],
      startTime: startTime,
      endTime: endTime,
      units: json['units'],
      source: source,
    );

    basal.basalProfile.targetId = json['basalProfile'];
    Basal.put(basal);

    return null;
  }
}
