import 'dart:async';
import 'dart:collection';

import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_ability.dart';
import 'package:aitrainer_app/model/exercise_plan.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/exercise_plan_template.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:aitrainer_app/service/exercise_plan_service.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/track.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

part 'test_set_edit_event.dart';
part 'test_set_edit_state.dart';

class TestSetEditBloc extends Bloc<TestSetEditEvent, TestSetEditState> {
  final String templateName;
  final String templateNameTranslation;
  late String templateDescription;
  final WorkoutTreeRepository workoutTreeRepository;
  final MenuBloc menuBloc;
  final List<ExerciseType> _exerciseTypes = [];
  final List<ExerciseType> _actualExerciseTypes = [];
  final HashMap<int, ExerciseType?> _exercisePlanDetails = HashMap();

  final HashMap<int, List<ExerciseType>?> _displayPlanDetails = HashMap();
  int? sliderIndex;

  TestSetEditBloc(
      {required this.templateName, required this.templateNameTranslation, required this.workoutTreeRepository, required this.menuBloc})
      : super(TestSetEditInitial()) {
    if (Cache().exercisePlanTemplates.isNotEmpty) {
      Cache().exercisePlanTemplates.forEach((element) {
        final ExercisePlanTemplate template = element as ExercisePlanTemplate;
        if (template.name == templateName) {
          templateDescription = template.descriptionTranslation;
          int index = 0;
          template.exerciseTypes.forEach((id) {
            final ExerciseType? exerciseType = Cache().getExerciseTypeById(id);
            if (exerciseType != null) {
              _exerciseTypes.add(exerciseType);
              _actualExerciseTypes.add(exerciseType);
              _exercisePlanDetails[index] = exerciseType;

              _displayPlanDetails[index] = [exerciseType];
              final WorkoutMenuTree? workoutTree = menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseType.exerciseTypeId);
              final List<WorkoutMenuTree>? alternativeMenuItems = menuBloc.menuTreeRepository.getWorkoutTreeAlternatives(workoutTree);
              if (alternativeMenuItems != null) {
                final List<ExerciseType>? actualList = _displayPlanDetails[index];
                alternativeMenuItems.forEach((element) {
                  actualList!.add(element.exerciseType!);
                });
              }
              index++;
            }
          });
        }
      });
    }
  }

  @override
  Stream<TestSetEditState> mapEventToState(TestSetEditEvent event) async* {
    try {
      if (event is TestSetEditChangeExerciseType) {
        yield TestSetEditLoading();
        ExerciseType? exerciseType;
        if (_exercisePlanDetails[event.indexKey] == null) {
          exerciseType = displayPlanDetails[event.indexKey][0];
        } else {
          exerciseType = displayPlanDetails[event.indexKey][event.index];
        }
        _exercisePlanDetails[event.indexKey] = exerciseType;
        this.sliderIndex = event.index;
        yield TestSetEditReady();
      } else if (event is TestSetEditDeleteExerciseType) {
        yield TestSetEditLoading();
        _exercisePlanDetails[event.indexKey] = null;

        yield TestSetEditReady();
      } else if (event is TestSetEditAddExerciseType) {
        yield TestSetEditLoading();
        ExerciseType exerciseType = displayPlanDetails[event.indexKey][0];
        _exercisePlanDetails[event.indexKey] = exerciseType;
        yield TestSetEditReady();
      } else if (event is TestSetEditSubmit) {
        yield TestSetEditLoading();
        ExercisePlan exercisePlan = ExercisePlan(templateNameTranslation, Cache().userLoggedIn!.customerId!);
        exercisePlan.private = true;
        exercisePlan.type = ExerciseAbility.mini_test_set.enumToString();
        exercisePlan.dateAdd = DateTime.now();
        ExercisePlan savedExercisePlan = await ExercisePlanApi().saveExercisePlan(exercisePlan);

        List<ExercisePlanDetail> details = [];
        for (var entry in _exercisePlanDetails.entries) {
          if (entry.value != null) {
            ExercisePlanDetail exercisePlanDetail = ExercisePlanDetail(entry.value!.exerciseTypeId);
            exercisePlanDetail.exercisePlanId = savedExercisePlan.exercisePlanId!;
            exercisePlanDetail.serie = 1;

            ExercisePlanDetail savedDetail = await ExercisePlanApi().saveExercisePlanDetail(exercisePlanDetail);
            exercisePlanDetail.exercisePlanDetailId = savedDetail.exercisePlanDetailId;
            exercisePlanDetail.exercises = [];
            details.add(exercisePlanDetail);
          }
        }
        Cache().saveActiveExercisePlan(exercisePlan, details);
        Track().track(TrackingEvent.test_set_edit, eventValue: templateName);
        yield TestSetEditSaved();
      } else if (event is TestSetEditAddError) {
        yield TestSetEditError(message: event.message);
      }
    } on Exception catch (e) {
      yield TestSetEditError(message: e.toString());
    }
  }

  List get exerciseTypes => this._exerciseTypes;
  List get actualExerciseTypes => this._actualExerciseTypes;
  HashMap get exercisePlanDetails => this._exercisePlanDetails;
  get displayPlanDetails => this._displayPlanDetails;

  void refreshActualPlan() {
    _actualExerciseTypes.removeRange(0, _actualExerciseTypes.length - 1);
    _exercisePlanDetails.keys.forEach((element) {
      final ExerciseType? exerciseType = Cache().getExerciseTypeById(element);
      if (exerciseType != null) {
        _actualExerciseTypes.add(exerciseType);
      }
    });
  }
}
