import 'dart:async';

import 'package:aitrainer_app/bloc/training_plan/training_plan_bloc.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer_training_plan_details.dart';
import 'package:aitrainer_app/model/exercise_plan_detail.dart';
import 'package:aitrainer_app/model/training_evaluation_exercise.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';

part 'training_evaluation_event.dart';
part 'training_evaluation_state.dart';

class TrainingEvaluationBloc extends Bloc<TrainingEvaluationEvent, TrainingEvaluationState> {
  final TrainingPlanBloc trainingPlanBloc;
  final String day;
  TrainingEvaluationBloc({required this.trainingPlanBloc, required this.day}) : super(TrainingEvaluationInitial());

  String duration = "-";
  String totalLift = "0";
  String maxTotalLift = "0";
  String maxRepeats = "0";
  DateTime? end;

  List<TrainingEvaluationExercise> evaluationList = [];

  @override
  Stream<TrainingEvaluationState> mapEventToState(
    TrainingEvaluationEvent event,
  ) async* {
    try {
      if (event is TrainingEvaluationLoad) {
        yield TrainingEvaluationLoading();
        await saveResult();
        getDuration();
        getTotalLift();
        getMaxRepeats();
        createEvaluationData();
        //getMaxTotalLift();
        if (end == null || DateTime.now().difference(end!).inMinutes > 5) {
          yield TrainingEvaluationReady();
        } else {
          yield TrainingEvaluationVictoryReady();
        }
      }
    } on Exception catch (e) {
      yield TrainingEvaluationError(message: e.toString());
    }
  }

  Future<void> saveResult() async {}

  void createEvaluationData() {
    if (trainingPlanBloc.getMyPlan() == null || trainingPlanBloc.getMyPlan()!.days[day] == null) {
      return;
    }

    trainingPlanBloc.getMyPlan()!.days[day]!.forEach((element) {
      //if (!element.state.equalsTo(ExercisePlanDetailState.extra)) {
      if (element.exerciseTypeId != null) {
        if (!findExerciseInEvaluationList(element.exerciseTypeId!)) {
          addEvaluationExercise(element);
        } else {
          //editEvaluationExercise(element);
        }
      }
      //}
    });
  }

  bool findExerciseInEvaluationList(int exerciseTypeId) {
    bool found = false;
    for (var exercise in evaluationList) {
      if (exercise.exerciseTypeId == exerciseTypeId) {
        found = true;
        break;
      }
    }
    return found;
  }

  void addEvaluationExercise(CustomerTrainingPlanDetails detail) {
    TrainingEvaluationExercise exercise = TrainingEvaluationExercise();
    exercise.exerciseTypeId = detail.exerciseTypeId!;
    exercise.name = detail.exerciseType!.nameTranslation;
    exercise.state = detail.state;

    if (detail.state.equalsTo(ExercisePlanDetailState.skipped)) {
      TrainingEvaluationExercise exercise = TrainingEvaluationExercise();
      exercise.exerciseTypeId = detail.exerciseTypeId!;
      exercise.name = detail.exerciseType!.nameTranslation;
      exercise.state = ExercisePlanDetailState.skipped;
      exercise.type = TrainingEvaluationExerciseType.weightBased;
      this.evaluationList.add(exercise);
    } else {
      if (detail.exerciseType!.unitQuantityUnit == null || detail.weight == null) {
        exercise.type = TrainingEvaluationExerciseType.repeatBased;
        exercise.repeats = detail.repeats;
        exercise.maxRepeats = getMaxRepeatsByExerciseType(detail.exerciseTypeId!);
        exercise.trend = getTrendEvaluationRepeats(exercise);
      } else {
        exercise.type = TrainingEvaluationExerciseType.weightBased;
        exercise.oneRepMax = Common.calculate1RM(detail.weight!, detail.repeats!.toDouble());
        exercise.max1RM = getMax1RMByExerciseType(detail.exerciseTypeId!);
        exercise.totalLift = (detail.weight! * detail.repeats!).round();
        exercise.maxTotalLift = getMaxTotalLiftByExerciseType(detail.exerciseTypeId!).round();
        exercise.trend = this.getTrendWeight(exercise.oneRepMax!, exercise.max1RM, exercise.totalLift, exercise.maxTotalLift);
      }
      exercise.trendText = getTrendText(exercise.trend!);
      exercise.trendColor = getTrendColor(exercise.trend!);
      this.evaluationList.add(exercise);
    }
  }

  double getTrendEvaluationRepeats(TrainingEvaluationExercise exercise) {
    double rate = exercise.repeats! / exercise.maxRepeats!;
    return rate;
  }

  String getTrendText(double rate) {
    if (rate > 1.10) {
      return "Strongly Growing";
    } else if (rate > 1.05) {
      return "Growing";
    } else if (rate < 0.90) {
      return "Strongly Sinking";
    } else if (rate < 0.95) {
      return "Sinking";
    } else {
      return "Stagnant";
    }
  }

  Color getTrendColor(double rate) {
    if (rate > 1.10) {
      return Colors.green[900]!;
    } else if (rate > 1.05) {
      return Colors.green[300]!;
    } else if (rate < 0.90) {
      return Colors.red[900]!;
    } else if (rate < 0.95) {
      return Colors.red[400]!;
    } else {
      return Colors.white;
    }
  }

  void getDuration() {
    if (trainingPlanBloc.getMyPlan() == null || trainingPlanBloc.getMyPlan()!.days[day] == null) {
      return;
    }
    int index = 0;
    DateTime? start;

    trainingPlanBloc.getMyPlan()!.days[day]!.forEach((element) {
      if (element.state.equalsTo(ExercisePlanDetailState.finished)) {
        if (index++ == 0) {
          start = element.exercises[0].dateAdd!;
        } else {
          if (element.exercises.length > 0) {
            this.end = element.exercises[element.exercises.length - 1].dateAdd!;
          }
        }
        //print("Exercise Date ${element.exercises[0].dateAdd!}");
      }
    });
    if (start == null) {
      this.duration = "00:00:00";
    } else if (this.end == null) {
      this.duration = "00:01:00";
      this.end = start;
    } else {
      this.duration = end!.difference(start!).inMinutes.toString();
    }
  }

  void getTotalLift() {
    if (trainingPlanBloc.getMyPlan() == null || trainingPlanBloc.getMyPlan()!.days[day] == null) {
      return;
    }
    double total = 0;
    trainingPlanBloc.getMyPlan()!.days[day]!.forEach((element) {
      if (element.state.equalsTo(ExercisePlanDetailState.finished) && element.exerciseType!.unitQuantityUnit != null) {
        total += element.weight! * element.repeats!;
      }
    });
    totalLift = total.toStringAsFixed(0);
  }

  void getMaxRepeats() {
    if (trainingPlanBloc.getMyPlan() == null || trainingPlanBloc.getMyPlan()!.days[day] == null) {
      return;
    }
    int max = 0;
    trainingPlanBloc.getMyPlan()!.days[day]!.forEach((element) {
      if (element.state.equalsTo(ExercisePlanDetailState.finished) && element.exerciseType!.unit != "second") {
        print("Repeats ${element.repeats}");
        if (max < element.repeats!) {
          max = element.repeats!;
        }
      }
    });
    maxRepeats = max.toStringAsFixed(0);
  }

  double getMaxTotalLiftByExerciseType(int exerciseTypeId) {
    if (Cache().getExercises() == null || Cache().getExercises()!.length == 0) {
      return 0;
    }
    double maxTotal = 0;
    Cache().getExercises()!.forEach((element) {
      if (element.exerciseTypeId == exerciseTypeId) {
        final double total = element.quantity! * element.unitQuantity!;
        if (maxTotal < total) {
          maxTotal = total;
        }
      }
    });

    return maxTotal;
  }

  double getMax1RMByExerciseType(int exerciseTypeId) {
    if (Cache().getExercises() == null || Cache().getExercises()!.length == 0) {
      return 0;
    }
    double max1RM = 0;
    Cache().getExercises()!.forEach((element) {
      if (element.exerciseTypeId == exerciseTypeId) {
        final double oneRepMax = Common.calculate1RM(element.unitQuantity!, element.quantity!);
        if (max1RM < oneRepMax) {
          max1RM = oneRepMax;
        }
      }
    });
    return max1RM;
  }

  int getMaxRepeatsByExerciseType(int exerciseTypeId) {
    if (Cache().getExercises() == null || Cache().getExercises()!.length == 0) {
      return 0;
    }
    int maxRepeats = 0;
    Cache().getExercises()!.forEach((element) {
      if (element.exerciseTypeId == exerciseTypeId) {
        final int repeats = element.quantity!.toInt();
        if (maxRepeats < repeats) {
          maxRepeats = repeats;
        }
      }
    });
    return maxRepeats;
  }

  double getOneRepMax(CustomerTrainingPlanDetails detail) {
    if (detail.weight == null) {
      return 0;
    }
    return Common.calculate1RM(detail.weight!, detail.repeats!.toDouble());
  }

  double getTotalLiftExercise(CustomerTrainingPlanDetails detail) {
    if (detail.weight == null) {
      return 0;
    }
    return detail.weight! * detail.repeats!;
  }

  double getTrendWeight(double oneRepMax, max1RM, totalLift, maxTotalLift) {
    double oneRepMaxRate = oneRepMax / max1RM;
    double totalLiftRate = totalLift / maxTotalLift;
    return (oneRepMaxRate + totalLiftRate) / 2;
  }
}