import 'dart:async';

import 'package:aitrainer_app/model/result.dart';
import 'package:aitrainer_app/repository/evaluation_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/exercise_result_repository.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';

//import 'package:health/health.dart';

part 'result_event.dart';
part 'result_state.dart';

class ResultBloc extends Bloc<ResultEvent, ResultState> with Logging, Trans {
  final ExerciseResultRepository resultRepository;
  final ExerciseRepository exerciseRepository;
  final EvaluationRepository evaluationRepository = EvaluationRepository();
  final BuildContext context;
  //List<HealthDataPoint> _healthDataList = [];
  DateTime? startTime;
  DateTime? endTime;
  /* final HealthFactory health = HealthFactory();
  final List<HealthDataType> types = [
    HealthDataType.ACTIVE_ENERGY_BURNED,
    HealthDataType.WATER,
    HealthDataType.STEPS,
    HealthDataType.HEART_RATE,
    HealthDataType.BASAL_ENERGY_BURNED,
    HealthDataType.BODY_TEMPERATURE,
    HealthDataType.HIGH_HEART_RATE_EVENT,
    HealthDataType.LOW_HEART_RATE_EVENT,
    HealthDataType.RESTING_HEART_RATE
  ]; */

  ResultBloc({required this.resultRepository, required this.exerciseRepository, required this.context}) : super(ResultInitial()) {
    this.startTime = exerciseRepository.start;
    this.endTime = exerciseRepository.end;
    if (this.startTime == null) {
      this.startTime = exerciseRepository.exercise!.dateAdd!;
      exerciseRepository.start = exerciseRepository.exercise!.dateAdd!;
    }
  }

  @override
  Stream<ResultState> mapEventToState(
    ResultEvent event,
  ) async* {
    try {
      if (event is ResultLoad) {
        yield ResultLoading();

        //await _fetchHealthData();
        _matchExerciseData();
        //await resultRepository.saveExerciseResults();
        yield ResultReady();
      }
    } on Exception catch (ex) {
      yield ResultError(error: ex.toString());
    }
  }

  void _matchExerciseData() {
    resultRepository.getResults().forEach((element) {
      element.dateFrom = startTime;
      element.dateTo = endTime;
      element.exerciseId = exerciseRepository.actualExerciseList![0].exerciseId;
      switch (element.item) {
        case ResultItem.bpm_avg:
          //element.data = _gethHealthDataPointValueAvg(HealthDataType.HEART_RATE);
          break;
        case ResultItem.bpm_min:
          //element.data = element.data = _gethHealthDataPointValueMin(HealthDataType.HEART_RATE);
          break;
        case ResultItem.bpm_max:
          //element.data = element.data = _gethHealthDataPointValueMax(HealthDataType.HEART_RATE);
          break;
        case ResultItem.calorie:
          //element.data = _gethHealthDataPointValueSum(HealthDataType.ACTIVE_ENERGY_BURNED);
          break;
        case ResultItem.development_percent_bodypart:
          break;
        case ResultItem.distance:
          if (exerciseRepository.exerciseType!.unit == "meter") {
            element.data = exerciseRepository.exercise!.quantity!;
          }
          break;
        case ResultItem.fatburn_percent:
          /* DateTime today = DateTime.now();
          int age = today.year - Cache().userLoggedIn.birthYear;
          double minBpm = (200 - age) * 0.6;
          double maxBpm = (200 - age) * 0.7;
          int counter = 0;
          int burnCounter = 0;
          _healthDataList.forEach((dataPoint) {
            if (dataPoint.type == HealthDataType.ACTIVE_ENERGY_BURNED) {
              if (dataPoint.value >= minBpm && dataPoint.value <= maxBpm) {
                burnCounter++;
              }
              counter++;
            }
          });
          if (counter > 0) {
            element.data = (burnCounter / counter * 100);
          } else {
            element.data = 0;
          } */
          break;
        case ResultItem.speed_max:
          break;
        case ResultItem.reps_volume:
          if (exerciseRepository.exerciseType!.unit == "repeat") {
            double value = 0;
            exerciseRepository.actualExerciseList!.forEach((actual) {
              value += actual.quantity!;
            });
            element.data = value;
          }
          break;
        case ResultItem.steps:
          element.data = 0; //_gethHealthDataPointValueSum(HealthDataType.STEPS);
          break;
        /*  case ResultItem.time:
          final Duration duration = this.endTime.difference(this.startTime);
          element.data = _printDuration(duration);
          break; */
        case ResultItem.weight_volume:
          if (exerciseRepository.exerciseType!.unitQuantityUnit == "kilogram") {
            double value = 0;
            exerciseRepository.actualExerciseList!.forEach((actual) {
              value += actual.quantity! * actual.unitQuantity!;
            });
            element.data = value;
          }
          break;
      }
    });
  }

  String printDuration(Duration duration, {isText = false, isDecimal = false}) {
    String twoDigits(int n) => n.toString().padLeft(2, "0");
    String twoDigitMinutes = twoDigits(duration.inMinutes);
    String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
    String twoDigitMilliSeconds = duration.inMilliseconds.remainder(1000).toString();
    if (isText) {
      if (isDecimal) {
        return "$twoDigitMinutes" + t("min") + "$twoDigitSeconds" + t("sec") + ":$twoDigitMilliSeconds" + '"';
      } else {
        return "$twoDigitMinutes" + t("min") + "$twoDigitSeconds" + t("sec");
      }
    } else {
      return "$twoDigitMinutes:$twoDigitSeconds:$twoDigitMilliSeconds" + '"';
    }
  }

  String printTime(double duration) {
    String twoDigits(int n) => n.toString().padLeft(1, "0");
    String twoDigitMinutes = twoDigits((duration ~/ 60).toInt());
    String twoDigitSeconds = (duration % 60).toStringAsFixed(0);

    return "$twoDigitMinutes " + t("minutes") + " $twoDigitSeconds";
  }

  /* double _gethHealthDataPointValueAvg(HealthDataType dataType) {
    double value = 0;
    double counter = 0;
    _healthDataList.forEach((dataPoint) {
      if (dataPoint.type == dataType) {
        value += dataPoint.value;
        counter++;
      }
    });
    double avg = 0;
    if (counter > 0) {
      avg = value / counter;
    }
    return avg;
  }

  double _gethHealthDataPointValueSum(HealthDataType dataType) {
    double value = 0;
    _healthDataList.forEach((dataPoint) {
      if (dataPoint.type == dataType) {
        value += dataPoint.value;
      }
    });
    return value;
  }

  double _gethHealthDataPointValueMax(HealthDataType dataType) {
    double max = 0;
    _healthDataList.forEach((dataPoint) {
      if (dataPoint.type == dataType) {
        if (max < dataPoint.value) {
          max = dataPoint.value;
        }
      }
    });
    return max;
  }

  double _gethHealthDataPointValueMin(HealthDataType dataType) {
    double min = 9999999;
    _healthDataList.forEach((dataPoint) {
      if (dataPoint.type == dataType) {
        if (min > dataPoint.value && dataPoint.value != 0) {
          min = dataPoint.value;
        }
      }
    });
    if (min == 9999999) {
      min = 0;
    }
    return min;
  } */

  /* Future<void> _fetchHealthData() async {
    if (health == null) {
      return;
    }
    try {
      log("Get Health data between " + startTime.toString() + " AND " + endTime.toString());
      _healthDataList = await health.getHealthDataFromTypes(this.startTime, this.endTime, types);
      _healthDataList.forEach((element) {
        log(element.toString());
      });
    } on Exception catch (e) {
      log("Caught exception in getHealthDataFromTypes: $e");
      throw Exception(e);
    }
  } */

  double calculate1RM({double percent = 0.75}) {
    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);
    double average = (rmWendler + rmOconner) / 2;

    return average * percent;
  }
}
