import 'dart:async';
import 'package:intl/intl.dart';

import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/model/fitness_state.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/service/logging.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';
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';

part 'exercise_new_event.dart';
part 'exercise_new_state.dart';

class ExerciseNewBloc extends Bloc<ExerciseNewEvent, ExerciseNewState> with Logging {
  final ExerciseRepository exerciseRepository;
  final CustomerRepository customerRepository;
  final MenuBloc menuBloc;
  late AnimationController bmiAnimationController;
  double quantity = 12;
  double unitQuantity = 30;
  double bmi = 0;
  double bmr = 0;
  double goalBMI = 0;
  double goalWeight = 0;
  double goalMilestoneBMI = 0;
  double goalMilestoneWeight = 0;
  double bmiAngle = 0;
  double bmiTop = 0;
  double bmiLeft = 0;
  late double weight;
  late double height;
  double bmrEnergy = 0;
  late int birthYear;
  late String fitnessLevel;
  bool changedWeight = false;
  bool changedSizes = false;

  final double baseWidth = 312;
  final double baseHeight = 675.2;
  double mediaWidth = 0;
  double mediaHeight = 0;

  String exerciseTask = "";

  final StopWatchTimer stopWatchTimer = StopWatchTimer(
    isLapHours: false,
  );
  late int timerValue;

  @override
  ExerciseNewBloc(
      {required this.exerciseRepository, required this.menuBloc, required this.customerRepository, required ExerciseType exerciseType})
      : super(ExerciseNewInitial()) {
    exerciseRepository.exerciseType = exerciseType;
    exerciseRepository.setUnit(exerciseType.unit);
    exerciseRepository.setUnitQuantity(unitQuantity);
    exerciseRepository.setQuantity(quantity);
    exerciseRepository.exercise!.exercisePlanDetailId = 0;
    exerciseRepository.start = DateTime.now();
    if (Cache().userLoggedIn != null) {
      customerRepository.customer = Cache().userLoggedIn!;
      weight = customerRepository.customer!.getProperty("Weight");
      height = customerRepository.customer!.getProperty("Height");
      birthYear = customerRepository.customer!.birthYear!;
      fitnessLevel = customerRepository.customer!.fitnessLevel!;
    }
    if (exerciseType.unit == "second") {
      stopWatchTimer.rawTime.listen((value) => {timerValue = value, this.setQuantity((value / 1000).toDouble())});
    }
  }

  void setQuantity(double quantity) {
    this.quantity = quantity;
    exerciseRepository.setQuantity(quantity);
  }

  @override
  Stream<ExerciseNewState> mapEventToState(ExerciseNewEvent event) async* {
    try {
      if (event is ExerciseNewLoad) {
        yield ExerciseNewLoading();
        yield ExerciseNewReady();
      } else if (event is ExerciseNewQuantityChange) {
        yield ExerciseNewLoading();
        log("Event quantity " + event.quantity.toStringAsFixed(0));
        this.setQuantity(event.quantity);
        yield ExerciseNewReady();
      } else if (event is ExerciseNewQuantityUnitChange) {
        yield ExerciseNewLoading();
        log("Event quantityUnit " + event.quantity.toStringAsFixed(0));
        exerciseRepository.setUnitQuantity(event.quantity);
        unitQuantity = event.quantity;
        yield ExerciseNewReady();
      } else if (event is ExerciseNewWeightChange) {
        yield ExerciseNewLoading();
        customerRepository.setWeight(event.value);
        changedWeight = true;
        weight = event.value;
        getBMI();
        getGoalBMI();
        getBMR();
        yield ExerciseNewReady();
      } else if (event is ExerciseNewFitnessLevelChange) {
        yield ExerciseNewLoading();
        customerRepository.setFitnessLevel(event.value);
        fitnessLevel = event.value;
        changedWeight = true;
        getBMR();
        yield ExerciseNewReady();
      } else if (event is ExerciseNewBirthyearChange) {
        yield ExerciseNewLoading();
        changedWeight = true;
        customerRepository.setBirthYear(event.value.toInt());
        birthYear = event.value;
        getBMR();
        yield ExerciseNewReady();
      } else if (event is ExerciseNewHeightChange) {
        yield ExerciseNewLoading();
        customerRepository.setHeight(event.value.toInt());
        changedWeight = true;
        height = event.value;
        getBMI();
        getGoalBMI();
        getBMR();
        yield ExerciseNewReady();
      } else if (event is ExerciseNewSaveWeight) {
        yield ExerciseNewLoading();
        customerRepository.saveCustomer();
        changedWeight = false;
        this.changedSizes = false;
        Cache().initBadges();
        Track().track(TrackingEvent.sizes);
        yield ExerciseNewReady();
      } else if (event is ExerciseNewSizeChange) {
        yield ExerciseNewLoading();
        this.changedSizes = true;
        this.customerRepository.updateSizes(event.propertyName, event.value);
        customerRepository.setCustomerProperty(event.propertyName, event.value);
        yield ExerciseNewReady();
      } else if (event is ExerciseNewSubmit) {
        yield ExerciseNewLoading();
        exerciseRepository.end = DateTime.now();
        await exerciseRepository.addExercise();
//        exerciseRepository.initExercise();
        menuBloc.add(MenuTreeDown(parent: 0));
        Cache().initBadges();
        Track().track(TrackingEvent.exercise_new, eventValue: exerciseRepository.exerciseType!.name);
        yield ExerciseNewSaved();
      } else if (event is ExerciseNewBMIAnimate) {
        yield ExerciseNewLoading();
        yield ExerciseNewReady();
      } else if (event is ExerciseNewAddError) {
        yield ExerciseNewLoading();
        yield ExerciseNewError(message: event.message);
      }
    } on Exception catch (e) {
      yield ExerciseNewError(message: e.toString());
    }
  }

  double getBMI() {
    if (height == 0 || weight == 0) {
      this.bmi = 0;
      return 0;
    }
    this.bmi = weight / (height * height / 10000);
    this.getGoalBMI();
    return this.bmi;
  }

  double getBMR() {
    var date = DateTime.now();
    if (height == 0 || weight == 0) {
      this.bmi = 0;
      return 0;
    }

    int year = int.parse(DateFormat(DateFormat.YEAR).format(date));

    if (customerRepository.customer!.sex == "m") {
      //66.47 + ( 13.75 × tömeg kg-ban ) + ( 5.003 × magasság cm-ben ) − ( 6.755 × életkor évben kifejezve )
      bmr = 66.47 + (13.75 * weight) + (5.003 * height) - (6.755 * (year - customerRepository.customer!.birthYear!));
    } else {
      //BMR = 655.1 + ( 9.563 × ömeg kg-ban ) + ( 1.85 × magasság cm-ben) − ( 4.676 × életkor évben kifejezve )
      bmr = 655.1 + (9.563 * weight) + (1.85 * height) - (4.676 * (year - customerRepository.customer!.birthYear!));
    }
    bmrEnergy = bmr;

    if (customerRepository.customer!.fitnessLevel == FitnessState.beginner) {
      bmr *= 1.2;
    } else if (customerRepository.customer!.fitnessLevel == FitnessState.intermediate) {
      bmr *= 1.375;
    } else if (customerRepository.customer!.fitnessLevel == FitnessState.advanced) {
      bmr *= 1.55;
    } else if (customerRepository.customer!.fitnessLevel == FitnessState.professional) {
      bmr *= 1.9;
    }
    return bmr;
  }

  double getGoalBMI() {
    if (this.bmi == 0) {
      getBMI();
    }
    if (bmi < 18.5) {
      goalMilestoneBMI = 19;
    } else if (bmi > 18.5 && bmi < 25) {
      goalMilestoneBMI = 21.75;
    } else if (bmi < 30 && bmi > 24.9) {
      goalMilestoneBMI = 24;

      bmiAngle = 7.2;
    } else if (bmi < 34.9 && 29.9 < bmi) {
      goalMilestoneBMI = 29;
    } else if (bmi > 35) {
      goalMilestoneBMI = 34;
    }

    goalBMI = 24;
    this.goalWeight = goalBMI * (height * height / 10000);
    this.goalMilestoneWeight = goalMilestoneBMI * (height * height / 10000);

    //print("Angle: " + bmiAngle.toStringAsFixed(1));

    return goalBMI;
  }
}
