import 'dart:collection';

import 'package:workouttest_util/util/app_language.dart';
import 'package:workouttest_util/model/cache.dart';
import 'package:workouttest_util/model/customer.dart';
import 'package:workouttest_util/model/exercise.dart';
import 'package:workouttest_util/model/exercise_type.dart';
import 'package:workouttest_util/model/workout_menu_tree.dart';
import 'package:workouttest_util/service/exercise_service.dart';
import 'package:intl/intl.dart';
import 'package:workouttest_util/util/logging.dart';

class ExerciseRepository with Logging {
  Exercise? exercise;
  Customer? customer;
  ExerciseType? exerciseType;
  List<Exercise>? exerciseList;
  List<Exercise>? exerciseLogList = [];
  List<Exercise>? actualExerciseList = [];
  bool noRegistration = false;

  double rmWendler = 0;
  double rmMcglothlin = 0;
  double rmLombardi = 0;
  double rmMayhew = 0;
  double rmOconner = 0;
  double rmWathen = 0;

  DateTime? start;
  DateTime? end;

  ExerciseRepository() {
    createNew();
  }

  createNew() {
    exercise = Exercise();
    exercise!.dateAdd = DateTime.now();
  }

  setQuantity(double quantity) {
    if (exercise == null) {
      createNew();
    }
    exercise!.quantity = quantity;
  }

  setUnitQuantity(double unitQuantity) {
    if (exercise == null) {
      createNew();
    }

    exercise!.unitQuantity = unitQuantity;
  }

  setUnit(String unit) {
    if (exercise == null) {
      createNew();
    }

    exercise!.unit = unit;
  }

  setDatetimeExercise(DateTime datetimeExercise) {
    if (exercise == null) {
      createNew();
    }

    exercise!.dateAdd = datetimeExercise;
  }

  double? get unitQuantity => exercise!.unitQuantity;

  double? get quantity => exercise!.quantity;

  Exercise? getExercise() => exercise;

  Future<Exercise> addExercise() async {
    if (customer == null) {
      throw Exception("Please log in");
    }
    final Exercise modelExercise = exercise!;
    modelExercise.customerId = customer!.customerId;
    modelExercise.exerciseTypeId = exerciseType!.exerciseTypeId;
    if (exerciseType!.unitQuantity != "1") {
      modelExercise.unitQuantity = null;
    }

    Exercise copy = modelExercise.copy();
    actualExerciseList!.add(copy);
    //final int index = actualExerciseList.length - 1;
    //print("$index. actual exercise " + actualExerciseList[index].toJson().toString());
    Exercise savedExercise = await ExerciseApi().addExercise(modelExercise);

    //actualExerciseList[index].exerciseId = savedExercise.exerciseId;
    if (customer!.customerId == Cache().userLoggedIn!.customerId) {
      Cache().addExercise(savedExercise);
    } else if (Cache().getTrainee() != null && customer!.customerId == Cache().getTrainee()!.customerId) {
      Cache().addExerciseTrainee(savedExercise);
    }

    return savedExercise;
  }

  void addExerciseNoRegistration() {
    final Exercise modelExercise = exercise!;
    modelExercise.exerciseTypeId = exerciseType!.exerciseTypeId;
    if (exerciseType!.unitQuantity != "1") {
      modelExercise.unitQuantity = null;
    }
    Exercise copy = modelExercise.copy();
    actualExerciseList!.add(copy);
    exerciseList = [];
    exerciseList!.add(copy);
    noRegistration = true;
  }

  void initExercise() {
    createNew();
    exerciseType = exerciseType;
    setUnit(exerciseType!.unit);
    exercise!.exerciseTypeId = exerciseType!.exerciseTypeId;
    setQuantity(12);
    setUnitQuantity(30);
    exercise!.exercisePlanDetailId = 0;
    exercise!.exerciseId = 0;
    start = DateTime.now();
  }

  Future<void> deleteExercise(Exercise exercise) async {
    await ExerciseApi().deleteExercise(exercise);
  }

  setCustomer(Customer customer) => customer = customer;

  setExerciseType(ExerciseType exerciseType) => exerciseType = exerciseType;

  Future<List<Exercise>> getExercisesByCustomer(int customerId) async {
    final results = await ExerciseApi().getExercisesByCustomer(customerId);
    exerciseList = results;
    if (Cache().userLoggedIn != null) {
      if (customerId == Cache().userLoggedIn!.customerId) {
        Cache().setExercises(exerciseList!);
      } else if (Cache().getTrainee() != null && customerId == Cache().getTrainee()!.customerId) {
        Cache().setExercisesTrainee(exerciseList!);
      }
    }
    return exerciseList!;
  }

  List<Exercise>? getExerciseList() {
    exerciseList = Cache().getExercises();
    return exerciseList;
  }

  List<Exercise>? getExerciseListTrainee() {
    exerciseList = Cache().getExercisesTrainee();
    return exerciseList;
  }

  String? nextMissingBaseExercise(SplayTreeMap sortedTree) {
    exerciseList ??= Cache().getExercises();

    if (exerciseList == null) {
      return null;
    }
    String? missingTreeName;
    String? foundTreeName;
    bool isBreak = false;

    sortedTree.forEach((key, list) {
      List<WorkoutMenuTree> listByMuscle = list as List<WorkoutMenuTree>;
      String treeName = key as String;
      treeName = treeName.substring(3);
      foundTreeName = null;
      for (var exercise in listByMuscle) {
        missingTreeName ??= treeName;
        if (exercise.base) {
          if (exerciseList != null) {
            for (var element in exerciseList!) {
              if (element.exerciseTypeId == exercise.exerciseTypeId) {
                foundTreeName = treeName;
                //print("Found " + foundTreeName + " Missing actual: " + missingTreeName);
                isBreak = true;
              }
            }
          }
        }
      }
      if (foundTreeName == null && !isBreak) {
        missingTreeName = treeName;
        isBreak = true;
      }
    });

    return missingTreeName;
  }

  void getBaseExerciseFinishedPercent() {
    List<int> checkedExerciseTypeId = [];
    List<int> baseTreeItem = [];
    List<int> checkedBaseTreeItem = [];
    int count1RMExercises = 0;
    LinkedHashMap<String, WorkoutMenuTree> tree = Cache().getWorkoutMenuTree();

    if (tree.isEmpty) {
      return;
    }

    tree.forEach((key, value) {
      WorkoutMenuTree treeItem = value;
      if (treeItem.exerciseType != null && treeItem.exerciseType!.base == true && !baseTreeItem.contains(treeItem.parent)) {
        baseTreeItem.add(treeItem.parent);
      }
    });

    exerciseList ??= Cache().getExercises();

    if (exerciseList == null) {
      return;
    }

    for (var element in exerciseList!) {
      Exercise exercise = element;
      if (!checkedExerciseTypeId.contains(exercise.exerciseTypeId)) {
        checkedExerciseTypeId.add(exercise.exerciseTypeId!);
        tree.forEach((key, value) {
          WorkoutMenuTree treeItem = value;
          if (treeItem.exerciseType != null &&
              treeItem.exerciseType!.base == true &&
              exercise.exerciseTypeId == treeItem.exerciseType!.exerciseTypeId &&
              !checkedBaseTreeItem.contains(treeItem.parent)) {
            //print ("id: " + exercise.exerciseTypeId.toString());
            checkedBaseTreeItem.add(treeItem.parent);
            count1RMExercises++;
          }
        });
      }
    }

    //print ("checkedExerciseTypeid: " + checkedExerciseTypeId.toString());
    //print ("baseTreeItem: " + baseTreeItem.toString());
    //print ("count1RMExercises: " + count1RMExercises.toString());
    final double percent = count1RMExercises / baseTreeItem.length;
    Cache().setPercentExercises(percent);
  }

  void getLastExercise() {
    List<Exercise>? exercises = getExerciseList();
    Exercise? lastExercise = exercises == null ? null : exercises[0];
    if (exercises != null) {
      for (var element in exercises) {
        Exercise actualExercise = element;
        if (actualExercise.dateAdd!.compareTo(lastExercise!.dateAdd!) > 0) {
          lastExercise = actualExercise;
        }
      }
    }
    exercise = lastExercise;
    customer = Cache().userLoggedIn!;
    exerciseType = getExerciseTypeById(exercise!.exerciseTypeId!);
    return;
  }

  ExerciseType? getExerciseTypeById(int exerciseTypeId) {
    ExerciseType? actualExerciseType;
    List<ExerciseType>? exercises = Cache().getExerciseTypes();
    if (exercises != null) {
      for (var element in exercises) {
        ExerciseType exerciseType = element;
        if (exerciseType.exerciseTypeId == exerciseTypeId) {
          actualExerciseType = exerciseType;
        }
      }
    }
    if (actualExerciseType == null) {
      throw Exception("Data error, no ExerciseType for exerciseTypeId $exerciseTypeId");
    }
    return actualExerciseType;
  }

  void getSameExercise(int exerciseTypeId, String day) {
    if (!noRegistration) {
      actualExerciseList = [];
    }
    if (exerciseList != null) {
      int index = 0;
      for (int i = 0; i < exerciseList!.length; i++) {
        Exercise exercise = exerciseList![i];
        final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
        if (exerciseTypeId == exercise.exerciseTypeId && exerciseDate == day && index < 4) {
          actualExerciseList!.add(exercise);
          index++;
        }
      }
    }
  }

  double calculate1RM(Exercise exercise) {
    double weight = exercise.unitQuantity!;
    double repeat = exercise.quantity!;
    if (weight == 0 || repeat == 0) {
      return 0;
    }

    double rmWendler = weight * repeat * 0.0333 + weight;
    double rmOconner = weight * (1 + repeat / 40);
    double average = (rmWendler + rmOconner) / 2;

    return average;
  }

  double getBest1RM(Exercise exercise) {
    double result = 0;
    if (exerciseList == null || exerciseList!.isEmpty) {
      exerciseList = Cache().getExercises();
    }

    final int exerciseTypeId = exercise.exerciseTypeId!;
    double toCompare = calculate1RM(exercise);
    result = toCompare;

    final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(DateTime.now());
    List<Exercise> oldExercises = [];
    if (exerciseList == null) {
      return toCompare;
    }
    for (var exercise in exerciseList!) {
      final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
      if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
        oldExercises.add(exercise);
      }
    }

    if (oldExercises.isNotEmpty) {
      oldExercises.sort((a, b) {
        double sumA = 0;
        double sumB = 0;
        if (a.unitQuantity != null && b.unitQuantity != null) {
          sumA = a.quantity! * a.unitQuantity!;
          sumB = b.quantity! * b.unitQuantity!;
        } else {
          sumA = a.quantity!;
          sumB = b.quantity!;
        }
        return sumA >= sumB ? 1 : -1;
      });

      double withCompare = calculate1RM(oldExercises.last);

      //result = toCompare >= withCompare ? (1 - toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
      result = toCompare >= withCompare ? toCompare : withCompare;
    }
    return result;
  }

  double getLast1RMPercent(Exercise exercise) {
    double result = 0;
    if (exerciseList == null || exerciseList!.isEmpty) {
      exerciseList = Cache().getExercises();
    }

    final int exerciseTypeId = exercise.exerciseTypeId!;
    double toCompare = calculate1RM(exercise);

    final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
    List<Exercise> oldExercises = [];

    if (exerciseList == null) {
      return result;
    }
    for (var exercise in exerciseList!) {
      final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
      if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
        oldExercises.add(exercise);
      }
    }

    if (oldExercises.isNotEmpty) {
      double withCompare = calculate1RM(oldExercises.first);
      result = toCompare >= withCompare ? (toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
    }
    return result;
  }

  double getBestVolume(Exercise exercise) {
    double result = 0;
    if (exerciseList == null || exerciseList!.isEmpty) {
      exerciseList = Cache().getExercises();
    }

    final int exerciseTypeId = exercise.exerciseTypeId!;
    double toCompare = exercise.unitQuantity != null ? exercise.quantity! * exercise.unitQuantity! : exercise.quantity!;
    result = toCompare;

    final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(DateTime.now());
    List<Exercise> oldExercises = [];
    if (exerciseList == null) {
      return toCompare;
    }
    for (var exercise in exerciseList!) {
      final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
      if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
        oldExercises.add(exercise);
      }
    }

    if (oldExercises.isNotEmpty) {
      oldExercises.sort((a, b) {
        double sumA = 0;
        double sumB = 0;
        if (a.unitQuantity != null && b.unitQuantity != null) {
          sumA = a.quantity! * a.unitQuantity!;
          sumB = b.quantity! * b.unitQuantity!;
        } else {
          sumA = a.quantity!;
          sumB = b.quantity!;
        }
        return sumA >= sumB ? 1 : -1;
      });

      double withCompare =
          oldExercises.last.unitQuantity != null ? oldExercises.last.quantity! * oldExercises.last.unitQuantity! : oldExercises.last.quantity!;

      //result = toCompare >= withCompare ? (1 - toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
      //print("Last Best: ${oldExercises.last} - result: $result");
      result = toCompare >= withCompare ? toCompare : withCompare;
    }
    return result;
  }

  double getLastExercisePercent(Exercise exercise) {
    double result = 0;
    if (exerciseList == null || exerciseList!.isEmpty) {
      exerciseList = Cache().getExercises();
    }

    final int exerciseTypeId = exercise.exerciseTypeId!;
    double toCompare = exercise.unitQuantity != null ? exercise.quantity! * exercise.unitQuantity! : exercise.quantity!;

    final String today = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
    List<Exercise> oldExercises = [];
    if (exerciseList == null) {
      return result;
    }
    for (var exercise in exerciseList!) {
      final String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
      if (exercise.exerciseTypeId == exerciseTypeId && exerciseDate.compareTo(today) < 0) {
        oldExercises.add(exercise);
      }
    }

    if (oldExercises.isNotEmpty) {
      double withCompare =
          oldExercises.first.unitQuantity != null ? oldExercises.first.quantity! * oldExercises.first.unitQuantity! : oldExercises.first.quantity!;

      result = toCompare >= withCompare ? (toCompare / withCompare) * 100 : (1 - toCompare / withCompare) * -100;
      log("Last Last: ${oldExercises.first} vs. $exercise -  - result: $result");
    }
    return result;
  }

  void sortByDate() {
    if (exerciseList == null || exerciseList!.isEmpty) {
      return;
    }

    exerciseList!.sort((a, b) {
      final String datePartA = DateFormat('yyyyMMdd', AppLanguage().appLocal.toString()).format(a.dateAdd!);
      String aId = "${datePartA}_${a.exerciseTypeId}";
      final String datePartB = DateFormat('yyyyMMdd', AppLanguage().appLocal.toString()).format(b.dateAdd!);
      String bId = "${datePartB}_${b.exerciseTypeId}";
      return bId.compareTo(aId);
    });

    exerciseLogList = [];
    String summary = "";

    String prevDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exerciseList![0].dateAdd!);
    int prevExerciseTypeId = exerciseList![0].exerciseTypeId!;
    Exercise prevExercise = exerciseList![0];
    int prevCount = 0;
    for (int i = 0; i < exerciseList!.length; i++) {
      Exercise exercise = exerciseList![i];
      int exerciseTypeId = exercise.exerciseTypeId!;
      String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd!);
      //print(" -- $prevExerciseTypeId - '$prevDate' against $exerciseTypeId - '$exerciseDate'");
      if (exerciseTypeId != prevExerciseTypeId || prevDate != exerciseDate) {
        ExerciseType? exerciseType = Cache().getExerciseTypeById(prevExercise.exerciseTypeId!);
        String unit = exerciseType != null && exerciseType.unitQuantityUnit != null ? exerciseType.unitQuantityUnit! : prevExercise.unit!;
        prevExercise.summary = "$summary $unit";
        exerciseLogList!.add(prevExercise);
        //print("Log add " + exercise.toJson().toString());
        summary = "";
        prevCount = 0;
      }
      String delimiter = "";
      if (prevCount > 0) delimiter = ", ";
      double quantity = exercise.quantity == null ? 0 : exercise.quantity!;
      summary += delimiter + quantity.toStringAsFixed(0);
      ExerciseType? exerciseType = Cache().getExerciseTypeById(exercise.exerciseTypeId!);
      //print("exerciseType " + (exerciseType == null ? "NULL" : exerciseType.name) + " ID " + exercise.exerciseTypeId.toString());
      if (exerciseType != null) {
        if (exerciseType.unitQuantity == "1") {
          summary += "x${exercise.unitQuantity!.toStringAsFixed(0)}";
        }
        //print(" --- sum  " + exerciseType.name + " $summary");
      }

      prevExerciseTypeId = exerciseTypeId;
      prevDate = exerciseDate;
      prevExercise = exercise;
      prevCount++;
    }
    prevExercise.summary = summary;
    exerciseLogList!.add(prevExercise);
  }

  List<Exercise> getExercisesByExerciseTypeId(int exerciseTypeId) {
    List<Exercise> list = [];
    List<Exercise>? allExercise = Cache().getExercises();
    if (allExercise == null) {
      return list;
    }
    for (var element in allExercise) {
      if (element.exerciseTypeId == exerciseTypeId) {
        list.add(element);
      }
    }
    return list;
  }
}