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/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 = List();
  final List<ExercisePlanDetail> memoryExercisePlanDetail = List();

  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 = List();
    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 = 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 == ExercisePlanDetailChange.delete ) {
          await MockExercisePlanApi()
            .deleteExercisePlanDetail(exercisePlanDetail.exercisePlanDetailId);
          Cache().deleteMyExercisePlanDetail(exercisePlanDetail);
        } else if ( exercisePlanDetail.change == ExercisePlanDetailChange.update ) {
          await MockExercisePlanApi()
            .updateExercisePlanDetail(exercisePlanDetail, exercisePlanDetail.exercisePlanDetailId);
          Cache().updateMyExercisePlanDetail(exercisePlanDetail);
        } else if ( exercisePlanDetail.change == ExercisePlanDetailChange.add ) {
          await MockExercisePlanApi()
            .saveExercisePlanDetail(exercisePlanDetail);
          Cache().addToMyExercisePlanDetails(exercisePlanDetail);
        }
      });
    }
  }
}