import 'dart:collection';

import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/model_change.dart';
import 'package:aitrainer_app/repository/exercise_plan_repository.dart';
import 'package:aitrainer_app/service/exercise_plan_service.dart';
import 'package:mockito/mockito.dart';

class MockExercisePlanApi extends Mock implements ExercisePlanApi {
  static final MockExercisePlanApi _singleton = MockExercisePlanApi._internal();
  factory MockExercisePlanApi() {
    return _singleton;
  }

  MockExercisePlanApi._internal();

  final List<ExercisePlan> memoryExercisePlan = [];
  final List<ExercisePlanDetail> memoryExercisePlanDetail = [];

  Future<ExercisePlan> saveExercisePlan(ExercisePlan plan) async {
    memoryExercisePlan.add(plan);
    plan.exercisePlanId = memoryExercisePlan.length;
    return plan;
  }

  Future<ExercisePlanDetail> saveExercisePlanDetail(ExercisePlanDetail detail) async {
    memoryExercisePlanDetail.add(detail);
    detail.exercisePlanDetailId = memoryExercisePlanDetail.length;
    return detail;
  }

  Future<ExercisePlanDetail> updateExercisePlanDetail(ExercisePlanDetail detail, int planDetailId) async {
    ExercisePlanDetail? updated;
    memoryExercisePlanDetail.forEach((element) {
      if (element.exercisePlanDetailId == planDetailId) {
        element = detail;
      }
    });

    return updated!;
  }

  Future<void> deleteExercisePlanDetail(int exercisePlanId) async {
    int index = -1;
    int countIndex = 0;
    memoryExercisePlanDetail.forEach((element) {
      if (element.exercisePlanId == exercisePlanId) {
        index = countIndex;
      }
      countIndex++;
    });
    if (index > -1) {
      memoryExercisePlanDetail.removeAt(index);
    }
  }

  Future<List<ExercisePlanDetail>> getExercisePlanDetail(int exercisePlanId) async {
    List<ExercisePlanDetail> foundList = [];
    memoryExercisePlanDetail.forEach((element) {
      if (element.exercisePlanId == exercisePlanId) {
        foundList.add(element);
      }
    });
    return foundList;
  }

  Future<ExercisePlan?> getLastExercisePlan(int customerId) async {
    ExercisePlan? found;
    memoryExercisePlan.forEach((element) {
      if (element.customerId == customerId) {
        found = element;
      }
    });
    return found;
  }
}

class SimExercisePlanRepository with ExercisePlanRepository {
  Future<void> getExercisePlanDetails() async {
    if (exercisePlan == null) {
      ExercisePlan? exercisePlan = await this.getLastExercisePlan();
      if (exercisePlan == null) {
        exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();
        return;
      }
    }

    List<ExercisePlanDetail> list = [];
    LinkedHashMap<int, ExercisePlanDetail> listCache = Cache().getMyExercisePlanDetails();
    if (listCache.length > 0) {
      exercisePlanDetails = listCache;
      return;
    } else {
      list = await MockExercisePlanApi().getExercisePlanDetail(exercisePlan!.exercisePlanId!);
    }

    exercisePlanDetails = LinkedHashMap<int, ExercisePlanDetail>();

    list.forEach((element) {
      newPlan = false;
      ExercisePlanDetail detail = element;
      exercisePlanDetails[detail.exerciseTypeId] = detail;
    });
    Cache().setMyExercisePlanDetails(exercisePlanDetails);

    return;
  }

  Future<ExercisePlan?> getLastExercisePlan() async {
    if (customerId == 0) {
      return null;
    }
    ExercisePlan? myExercisePlan = Cache().getMyExercisePlan();
    if (myExercisePlan != null) {
      newPlan = false;
      return myExercisePlan;
    }

    exercisePlan = await MockExercisePlanApi().getLastExercisePlan(customerId);
    newPlan = (exercisePlan == null);
    Cache().setMyExercisePlan(exercisePlan!);
    return exercisePlan;
  }

  Future<void> saveExercisePlan() async {
    if (exercisePlan == null) {
      if (Cache().userLoggedIn == null) {
        throw Exception("please log in");
      }

      String exercisePlanName;
      if (this.customerId == Cache().userLoggedIn!.customerId) {
        exercisePlanName = Cache().userLoggedIn!.name! + " private";
      } else {
        exercisePlanName = Cache().getTrainee()!.name! + " " + Cache().getTrainee()!.firstname! + " private";
      }

      exercisePlan = ExercisePlan(exercisePlanName, this.customerId);
    }
    if (newPlan) {
      exercisePlan!.dateAdd = DateTime.now();
      exercisePlan!.private = true;

      ExercisePlan savedExercisePlan = await MockExercisePlanApi().saveExercisePlan(exercisePlan!);

      LinkedHashMap<int, ExercisePlanDetail> savedExercisePlanDetails = LinkedHashMap();
      exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
        exercisePlanDetail.exercisePlanId = savedExercisePlan.exercisePlanId!;

        ExercisePlanDetail savedDetail = await MockExercisePlanApi().saveExercisePlanDetail(exercisePlanDetail);

        savedExercisePlanDetails[savedDetail.exerciseTypeId] = savedDetail;
      });

      exercisePlan = savedExercisePlan;
    } else {
      await MockExercisePlanApi().updateExercisePlan(exercisePlan!, exercisePlan!.exercisePlanId!);

      exercisePlanDetails.forEach((exerciseTypeId, exercisePlanDetail) async {
        if (exercisePlanDetail.change == ModelChange.delete) {
          await MockExercisePlanApi().deleteExercisePlanDetail(exercisePlanDetail.exercisePlanDetailId!);
          Cache().deleteMyExercisePlanDetail(exercisePlanDetail);
        } else if (exercisePlanDetail.change == ModelChange.update) {
          await MockExercisePlanApi().updateExercisePlanDetail(exercisePlanDetail, exercisePlanDetail.exercisePlanDetailId!);
          Cache().updateMyExercisePlanDetail(exercisePlanDetail);
        } else if (exercisePlanDetail.change == ModelChange.add) {
          await MockExercisePlanApi().saveExercisePlanDetail(exercisePlanDetail);
          Cache().addToMyExercisePlanDetails(exercisePlanDetail);
        }
      });
    }
  }
}