import 'package:aitrainer_app/repository/mautic_repository.dart';
import 'package:intl/intl.dart';
import 'package:flutter/foundation.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/material.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 = -1;
  double unitQuantity = -1;
  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;
  double? weight;
  double? height;
  double bmrEnergy = 0;
  int? birthYear;
  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;

  late ExerciseType exerciseType;

  @override
  ExerciseNewBloc({required this.exerciseRepository, required this.menuBloc, required this.customerRepository, required this.exerciseType})
      : super(ExerciseNewInitial()) {
    this._load();
    on<ExerciseNewQuantityChange>(_onQuantityChange);
    on<ExerciseNewQuantityUnitChange>(_onQuantityUnitChange);
    on<ExerciseNewWeightChange>(_onWeightChange);
    on<ExerciseNewFitnessLevelChange>(_onFitnessLevelChange);
    on<ExerciseNewBirthyearChange>(_onBirthyearChange);
    on<ExerciseNewHeightChange>(_onHeightChange);
    on<ExerciseNewSaveWeight>(_onSaveWeight);
    on<ExerciseNewSizeChange>(_onSizeChange);
    on<ExerciseNewSubmit>(_onSubmit);
    on<ExerciseNewSubmitNoRegistration>(_onSubmitNoRegistration);
    on<ExerciseNewBMIAnimate>(_onBMIAnimate);
    on<ExerciseNewAddError>(_onAddError);
  }

  void _load() {
    exerciseRepository.exerciseType = this.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 _onQuantityChange(ExerciseNewQuantityChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    log("Event quantity " + event.quantity.toStringAsFixed(0));
    this.setQuantity(event.quantity);
    emit(ExerciseNewReady());
  }

  void _onQuantityUnitChange(ExerciseNewQuantityUnitChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    log("Event quantityUnit " + event.quantity.toStringAsFixed(0));
    exerciseRepository.setUnitQuantity(event.quantity);
    unitQuantity = event.quantity;
    emit(ExerciseNewReady());
  }

  void _onWeightChange(ExerciseNewWeightChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    customerRepository.setWeight(event.value);
    changedWeight = true;
    weight = event.value;
    getBMI();
    getGoalBMI();
    getBMR();
    emit(ExerciseNewReady());
  }

  void _onFitnessLevelChange(ExerciseNewFitnessLevelChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    customerRepository.setFitnessLevel(event.value);
    fitnessLevel = event.value;
    changedWeight = true;
    getBMR();
    emit(ExerciseNewReady());
  }

  void _onBirthyearChange(ExerciseNewBirthyearChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    changedWeight = true;
    customerRepository.setBirthYear(event.value.toInt());
    birthYear = event.value;
    emit(ExerciseNewReady());
  }

  void _onHeightChange(ExerciseNewHeightChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    customerRepository.setHeight(event.value.toInt());
    changedWeight = true;
    height = event.value;
    getBMI();
    getGoalBMI();
    getBMR();
    emit(ExerciseNewReady());
  }

  void _onSaveWeight(ExerciseNewSaveWeight event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    customerRepository.saveCustomer();
    changedWeight = false;
    this.changedSizes = false;
    Cache().initBadges();
    Track().track(TrackingEvent.sizes);
    emit(ExerciseNewReady());
  }

  void _onSizeChange(ExerciseNewSizeChange event, Emitter<ExerciseNewState> emit) {
    emit(ExerciseNewLoading());
    this.changedSizes = true;
    this.customerRepository.updateSizes(event.propertyName, event.value);
    customerRepository.setCustomerProperty(event.propertyName, event.value);
    emit(ExerciseNewReady());
  }

  void _onSubmit(ExerciseNewSubmit event, Emitter<ExerciseNewState> emit) async {
    emit(ExerciseNewLoading());
    if (quantity == -1 || unitQuantity == -1) {
      emit(ExerciseNewReady());
      throw Exception("Please type in a real number");
    }

    exerciseRepository.end = DateTime.now();
    await exerciseRepository.addExercise();
    if (kReleaseMode) {
      MauticRepository mauticRepository = MauticRepository(customerRepository: customerRepository);
      await mauticRepository.sendMauticExercise();
    }
    menuBloc.add(MenuTreeDown(parent: 0));
    Cache().initBadges();
    Track().track(TrackingEvent.exercise_new, eventValue: exerciseRepository.exerciseType!.name);
    emit(ExerciseNewSaved());
  }

  void _onSubmitNoRegistration(ExerciseNewSubmitNoRegistration event, Emitter<ExerciseNewState> emit) async {
    emit(ExerciseNewLoading());
    exerciseRepository.addExerciseNoRegistration();
    menuBloc.add(MenuTreeDown(parent: 0));
    Track().track(TrackingEvent.exercise_new_no_registration, eventValue: exerciseRepository.exerciseType!.name);
    emit(ExerciseNewSaved());
  }

  void _onBMIAnimate(ExerciseNewBMIAnimate event, Emitter<ExerciseNewState> emit) async {
    emit(ExerciseNewLoading());
    emit(ExerciseNewReady());
  }

  void _onAddError(ExerciseNewAddError event, Emitter<ExerciseNewState> emit) async {
    emit(ExerciseNewLoading());
    emit(ExerciseNewReady());
  }

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

  double getBMI() {
    if (weight == null || height == null || 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 (weight == null ||
        height == null ||
        height == 0 ||
        weight == 0 ||
        customerRepository.customer == null ||
        customerRepository.customer!.birthYear == null) {
      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 (weight == null || height == null) {
      return 0;
    }
    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;
  }
}