import 'dart:async';
import 'package:aitrainer_app/bloc/timer/timer_bloc.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

part 'exercise_control_event.dart';

part 'exercise_control_state.dart';

enum ExerciseTypeLocal { oneRepMax, endurance }

extension ExerciseTypeLocalExt on ExerciseTypeLocal {
  String toStr() => this.toString().split(".").last;
  bool equalsTo(ExerciseTypeLocal type) => this.toString() == type.toString();
  bool equalsStringTo(String type) => this.toStr() == type;
}

class ExerciseControlBloc extends Bloc<ExerciseControlEvent, ExerciseControlState> {
  final TimerBloc timerBloc;
  final ExerciseRepository exerciseRepository;
  final bool readonly;
  int step = 1;

  late double initialRM;
  late double unitQuantity;
  late double quantity;
  late double origQuantity;
  late double oneRepQuantity;
  late double oneRepUnitQuantity;

  double? firstQuantity; // quantity of the first test
  double? firstUnitQuantity; // unit quantity of the first test

  double scrollOffset = 0;
  late ExerciseTypeLocal exerciseTypeLocal;

  @override
  ExerciseControlBloc({required this.exerciseRepository, required this.readonly, required this.timerBloc})
      : super(ExerciseControlInitial()) {
    print("Exercise ${exerciseRepository.exercise!.toJson()}");

    exerciseTypeLocal = ExerciseTypeLocal.oneRepMax;
    oneRepQuantity = exerciseRepository.exercise!.quantity!;
    if (oneRepQuantity > 25) {
      exerciseTypeLocal = ExerciseTypeLocal.endurance;
    }
    oneRepUnitQuantity = exerciseRepository.exercise!.unitQuantity!;
    initialRM = this.calculate1RM(percent75: false);
    unitQuantity = this.calculate1RM(percent75: true).roundToDouble();
    quantity = 12;
    if (oneRepQuantity > 25) {
      quantity = 35;
    }
    origQuantity = quantity;

    exerciseRepository.setUnitQuantity(unitQuantity);
    exerciseRepository.setQuantity(quantity);

    timerBloc.add(TimerStart(duration: 300));
  }

  @override
  Stream<ExerciseControlState> mapEventToState(ExerciseControlEvent event) async* {
    try {
      /* if (event is ExerciseControlLoad) {
        yield ExerciseControlLoading();
        step = 1;
        yield ExerciseControlReady();
      } else  */
      if (event is ExerciseControlQuantityChange) {
        //yield ExerciseControlLoading();
        if (event.step == step) {
          exerciseRepository.setQuantity(event.quantity);
          quantity = event.quantity;
        }
        //yield ExerciseControlReady();
      } else if (event is ExerciseControlUnitQuantityChange) {
        yield ExerciseControlLoading();
        print("event step ${event.step} quantity ${event.quantity}");
        if (event.step == step) {
          this.unitQuantity = event.quantity;
          exerciseRepository.setUnitQuantity(event.quantity);
          unitQuantity = event.quantity;
          quantity = calculateQuantityByUnitQuantity().toDouble();
          exerciseRepository.setQuantity(quantity);
          origQuantity = quantity;
        }
        yield ExerciseControlReady();
      } else if (event is ExerciseControlSubmit) {
        yield ExerciseControlLoading();
        if (event.step == step) {
          step++;
          scrollOffset = step * 400.0;

          quantity = origQuantity;
          if (exerciseRepository.exercise!.quantity == null) {
            exerciseRepository.setQuantity(quantity);
          }
          exerciseRepository.end = DateTime.now();
          await exerciseRepository.addExercise();
          //exerciseRepository.initExercise();

          step <= 3 ? timerBloc.add(TimerStart(duration: 300)) : timerBloc.add(TimerEnd(duration: 300));
        }
        yield ExerciseControlReady();
      }
    } on Exception catch (e) {
      yield ExerciseControlError(message: e.toString());
    }
  }

  double calculate1RM({bool percent75 = true}) {
    if (exerciseRepository.exercise == null) {
      exerciseRepository.getLastExercise();
    }
    double weight = exerciseRepository.exercise!.unitQuantity!;
    double repeat = exerciseRepository.exercise!.quantity!;
    if (weight == 0 || repeat == 0) {
      return 0;
    }

    double rmWendler = weight * repeat * 0.0333 + weight;
    double rmOconner = weight * (1 + repeat / 40);
    print("Weight: $weight repeat: $repeat,  $rmWendler, Oconner: $rmOconner");
    double average = (rmWendler + rmOconner) / 2;

    double serieQuantity = 0.75;
    if (exerciseTypeLocal.equalsTo(ExerciseTypeLocal.endurance)) {
      serieQuantity = 0.5;
    }

    return percent75 ? average * serieQuantity : average;
  }

  int calculateQuantityByUnitQuantity() {
    double weight = oneRepUnitQuantity;
    double repeat = oneRepQuantity;
    final double rmWendler = weight * repeat * 0.0333 + weight;
    final double rmOconner = weight * (1 + repeat / 40);
    print("Weight: $weight oneRepQuantity: $repeat,  $rmWendler, Oconner: $rmOconner");

    weight = exerciseRepository.exercise!.unitQuantity!;
    repeat = exerciseRepository.exercise!.quantity!;

    final double repeatWendler = (rmWendler - weight) / 0.0333 / weight;
    final double repeatOconner = (rmOconner / weight - 1) * 40;
    final newRepeat = ((repeatOconner + repeatWendler) / 2).ceil();
    print("Initial 1RM: $initialRM Weight: $weight repeatWendler: $repeatWendler repeat Oconner: $repeatOconner. NEW REPEAT: $newRepeat");
    return newRepeat;
  }
}