import 'dart:collection';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_ability.dart';
import 'package:aitrainer_app/model/exercise_tree.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:flutter/material.dart';

enum Antagonist { chest, biceps, triceps, back, shoulders, core, thigh, calf }

extension AntagonistExt on Antagonist {
  bool equalsTo(Antagonist type) => this.toString() == type.toString();
  bool equalsStringTo(String type) => this.toString() == type;
  String enumToString() => this.toString().split(".").last;
}

class WorkoutTreeRepository with Logging {
  final LinkedHashMap<String, WorkoutMenuTree> tree = LinkedHashMap<String, WorkoutMenuTree>();
  SplayTreeMap sortedTree = SplayTreeMap<String, List<WorkoutMenuTree>>();
  bool? isEnglish;
  WorkoutType? workoutType;
  final List<WorkoutMenuTree> menuAsExercise = [];

  void createTree() {
    //if (Cache().getExerciseTree().length > 0 || Cache().getWorkoutMenuTree().length > 0) return;
    isEnglish = AppLanguage().appLocal == Locale('en');
    List<ExerciseTree>? exerciseTree = Cache().getExerciseTree();
    List<ExerciseType>? exerciseTypes = Cache().getExerciseTypes();
    if (exerciseTree == null || exerciseTypes == null) {
      return;
    }

    log("** Start creating tree on lang: ${AppLanguage().appLocal.languageCode}");

    exerciseTree.sort((a, b) => a.sort!.compareTo(b.sort!));

    exerciseTree.forEach((treeItem) async {
      if (treeItem.active == true) {
        String treeName = isEnglish! ? treeItem.name : treeItem.nameTranslation;

        bool is1RM = treeItem.internalName != null && treeItem.internalName!.contains("one_rep_max") ? true : false;
        if (!is1RM) {
          is1RM = this.isParent1RM(treeItem.parentId);
        }

        bool isRunning = treeItem.name == "Cardio" ? true : false;
        if (isRunning == false && treeItem.parentId != 0) {
          isRunning = isParentRunning(treeItem.parentId);
        }
        WorkoutMenuTree? parent = getParentItem(treeItem.parentId);
        WorkoutMenuTree menuItem = WorkoutMenuTree(
            treeItem.treeId,
            treeItem.parentId,
            treeName,
            treeItem.imageUrl,
            Colors.white,
            30,
            false,
            0,
            null,
            false,
            is1RM,
            isRunning,
            treeItem.name,
            parent != null ? parent.name : "",
            parent != null ? parent.nameEnglish : "",
            treeItem.sort!,
            treeItem.internalName != null ? treeItem.internalName! : "");
        menuItem = this.setWorkoutTypes(menuItem, treeItem);
        this.tree[treeItem.name + "_" + treeItem.parentId.toString()] = menuItem;
        //log("WorkoutMenuTree item ${menuItem.toJson()}");
      }
    });

    exerciseTypes.forEach((exerciseType) async {
      if (!(exerciseType.imageUrl.isEmpty || exerciseType.name.isEmpty || exerciseType.nameTranslation.isEmpty) &&
          exerciseType.active == true) {
        String exerciseTypeName = isEnglish! ? exerciseType.name : exerciseType.nameTranslation;
        //String assetImage = await _buildImage(exerciseType.imageUrl); //'asset/menu/' + exerciseType.imageUrl.substring(7);

        if (exerciseType.parents.isNotEmpty) {
          exerciseType.parents.forEach((parentId) {
            bool is1RM = this.isParent1RM(parentId);
            if (is1RM) {
              exerciseType.setAbility(ExerciseAbility.oneRepMax);
            }
            bool isRunning = this.isParentRunning(parentId);
            if (isRunning) {
              is1RM = false;
              exerciseType.setAbility(ExerciseAbility.running);
            }
            WorkoutMenuTree? parent = getParentItem(parentId);
            WorkoutMenuTree menuItem = WorkoutMenuTree(
                exerciseType.exerciseTypeId,
                parentId,
                exerciseTypeName,
                exerciseType.imageUrl,
                Colors.white,
                24,
                true,
                exerciseType.exerciseTypeId,
                exerciseType,
                exerciseType.base,
                is1RM,
                isRunning,
                exerciseType.name,
                parent != null ? parent.name : "",
                parent != null ? parent.nameEnglish : "",
                0,
                "");
            this.tree[exerciseType.name] = menuItem;
            if (isRunning || is1RM) {
              menuAsExercise.add(menuItem);
            }
            //log("ExerciseType in Menu item ${exerciseType.toJson()} is1RM: $is1RM");
          });
        } else {
          //log("No Parents " + exerciseType.toJson().toString());
        }
      } else {
        //log("ExerciseType missing data: " + exerciseType.exerciseTypeId.toString());
      }
    });

    Cache().setWorkoutMenuTree(tree);
    ExerciseRepository exerciseRepository = ExerciseRepository();
    exerciseRepository.getBaseExerciseFinishedPercent();
    menuAsExercise.sort((a, b) => a.name.compareTo(b.name));
  }

  WorkoutMenuTree setWorkoutTypes(WorkoutMenuTree menu, ExerciseTree treeItem) {
    WorkoutType.values.forEach((workoutType) {
      if (workoutType.equalsString(treeItem.name) || existWorkoutTypeInTree(workoutType)) {
        menu.workoutTypes.add(workoutType);
      }
    });
    return menu;
  }

  bool existWorkoutTypeInTree(WorkoutType workoutType) {
    bool exists = false;
    this.tree.forEach((key, treeItem) {
      if (workoutType.equalsString(treeItem.name)) {
        exists = true;
      }
    });
    return exists;
  }

  bool isParentRunning(int treeId) {
    bool isTreeItemRunning = false;

    this.tree.forEach((key, value) {
      WorkoutMenuTree treeItem = value;
      if (treeItem.id == treeId) {
        isTreeItemRunning = isTreeItemRunning || treeItem.isRunning;
        //log (treeItem.name + " 1RM " + treeItem.is1RM.toString() );
      }
    });

    return isTreeItemRunning;
  }

  bool isParent1RM(int treeId) {
    bool isTreeItem1RM = false;

    this.tree.forEach((key, value) {
      WorkoutMenuTree treeItem = value;
      if (treeItem.id == treeId) {
        isTreeItem1RM = isTreeItem1RM || treeItem.is1RM;
        //log(treeItem.name + " 1RM " + treeItem.is1RM.toString());
      }
    });

    return isTreeItem1RM;
  }

  bool isChild(int parentId) {
    bool isChild = true;

    this.getBranch(parentId).forEach((key, value) {
      WorkoutMenuTree workoutTree = value;
      isChild = isChild && workoutTree.child;
    });
    //log("Check child " + parentId.toString() + " child: " + isChild.toString());
    return isChild;
  }

  bool isChildAndGym(int parentId) {
    bool isChild = true;
    bool isGym = true;

    this.getBranch(parentId).forEach((key, value) {
      WorkoutMenuTree workoutTree = value;
      isChild = isChild && workoutTree.child;
      isGym = isGym && (workoutTree.is1RM);
    });
    return isChild && isGym;
  }

  LinkedHashMap<String, WorkoutMenuTree> getBranch(int parent, {bool filtering = false}) {
    LinkedHashMap<String, WorkoutMenuTree> branch = LinkedHashMap<String, WorkoutMenuTree>();
    tree.forEach((key, value) {
      WorkoutMenuTree workoutTree = value;
      if (parent == workoutTree.parent) {
        branch[key] = value;
      }
    });
    return branch;
  }

  List<WorkoutMenuTree> getBranchList(int parent) {
    List<WorkoutMenuTree> branch = [];
    tree.forEach((key, value) {
      WorkoutMenuTree workoutTree = value;
      if (parent == workoutTree.parent) {
        branch.add(workoutTree);
      }
    });
    return branch;
  }

  WorkoutMenuTree? getParentItem(int parent) {
    WorkoutMenuTree? parentItem;
    tree.forEach((key, value) {
      WorkoutMenuTree workoutTree = value;
      if (parent == workoutTree.id) {
        parentItem = workoutTree;
      }
    });
    return parentItem;
  }

  WorkoutMenuTree? getMenuItemByExerciseTypeId(int exerciseTypeId) {
    WorkoutMenuTree? menuItem;
    this.menuAsExercise.forEach((element) {
      if (element.exerciseTypeId == exerciseTypeId) {
        menuItem = element;
      }
    });
    return menuItem;
  }

  List<WorkoutMenuTree>? getWorkoutTreeAlternatives(WorkoutMenuTree? workoutMenuTree) {
    if (workoutMenuTree == null) {
      return null;
    }
    final List<ExerciseType>? alternatives = this.getExerciseTypeAlternatives(workoutMenuTree.exerciseTypeId);
    if (alternatives == null) {
      return null;
    }

    List<WorkoutMenuTree> list = [];
    alternatives.forEach((element) {
      final WorkoutMenuTree? alternativeMenuItem = this.getMenuItemByExerciseTypeId(element.exerciseTypeId);
      if (alternativeMenuItem != null) {
        list.add(alternativeMenuItem);
      }
    });

    return list;
  }

  List<ExerciseType> getExerciseTypeAlternatives(int? exerciseTypeId) {
    if (exerciseTypeId == null || exerciseTypeId <= 0) {
      return [];
    }
    List<ExerciseType> list = [];
    List<ExerciseType>? exerciseTypes = Cache().getExerciseTypes();
    if (exerciseTypes != null) {
      exerciseTypes.forEach((exerciseType) {
        if (exerciseType.alternatives.isNotEmpty) {
          exerciseType.alternatives.forEach((childId) {
            if (childId == exerciseTypeId) {
              list.add(exerciseType);
            }
          });
        }
      });
    }

    return list;
  }

  String getAntagonistSort(String type) {
    String found = "";
    for (int i = 0; i < Antagonist.values.length; i++) {
      if (type.toLowerCase().contains((Antagonist.values[i]).enumToString())) {
        found = (i + 1).toString();
      }
    }
    return found;
  }

  void sortByMuscleType() {
    sortedTree = SplayTreeMap<String, List<WorkoutMenuTree>>();
    tree.forEach((key, value) {
      WorkoutMenuTree workoutTree = value;
      if (!workoutTree.nameEnglish.contains('Muscle Build') &&
          !workoutTree.nameEnglish.contains('Strength') &&
          workoutTree.is1RM &&
          workoutTree.exerciseTypeId == 0) {
        String treeName = getAntagonistSort(workoutTree.nameEnglish) + ". " + workoutTree.name;
        //print("TreeName $treeName ${workoutTree.name}");
        sortedTree[treeName] = this.getBranchList(workoutTree.id);
      }
    });

    return;
  }

  int getMissingTreeIdByName(String name) {
    int missingId = 0;
    tree.forEach((key, value) {
      WorkoutMenuTree item = value;
      if (item.name == name || name == item.nameEnglish) {
        missingId = item.id;
      }
    });
    return missingId;
  }
}
