import 'dart:async';

import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer_property.dart';
import 'package:aitrainer_app/model/property.dart';
import 'package:aitrainer_app/repository/customer_repository.dart';
import 'package:aitrainer_app/service/customer_service.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

part 'bodytype_event.dart';
part 'bodytype_state.dart';

class BodytypeBloc extends Bloc<BodytypeEvent, BodytypeState> {
  static const int numberQuestions = 22;
  final CustomerRepository repository;
  final List<String> questions = [];
  final List<int> answers = [];
  final weights = List.generate(numberQuestions, (i) => []..length = 3, growable: false);

  BodytypeBloc({required this.repository}) : super(BodytypeInitial()) {
    questions.add("1. Basicly I am skinny and bonny");
    questions.add("2. question");
    questions.add("3. question");
    questions.add("4. question");
    questions.add("5. question");
    questions.add("6. question");
    questions.add("7. question");
    questions.add("8. question");
    questions.add("9. question");
    questions.add("10. question");
    questions.add("11. question");
    questions.add("12. question");
    questions.add("13. question");
    questions.add("14. question");
    questions.add("15. question");
    questions.add("16. question");
    questions.add("17. question");
    questions.add("18. question");
    questions.add("19. question");
    questions.add("20. question");
    questions.add("21. question");
    questions.add("22. question");
    for (int i = 0; i < numberQuestions; i++) {
      answers.add(0);
    }
    weights[0] = [0, 3, 7];
    weights[1] = [0, 3, 7];
    weights[2] = [0, 3, 7];
    weights[3] = [0, 3, 7];
    weights[4] = [0, 3, 7];
    weights[5] = [0, 3, 7];
    weights[6] = [0, 3, 7];

    weights[7] = [3, 7, 0];
    weights[8] = [5, 7, 0];
    weights[9] = [0, 7, 3];
    weights[10] = [5, 5, 0];
    weights[11] = [7, 5, 0];
    weights[12] = [7, 5, 0];
    weights[13] = [5, 5, 0];

    weights[14] = [7, 3, 0];
    weights[15] = [7, 3, 0];
    weights[16] = [7, 3, 0];
    weights[17] = [7, 3, 0];
    weights[18] = [7, 0, 0];
    weights[19] = [7, 0, 0];
    weights[20] = [7, 3, 0];
    weights[21] = [7, 3, 0];

    final double value = repository.getCustomerPropertyValue(PropertyEnum.Ectomorph.toStr());

    _ecto = value.toInt();
    _mezo = repository.getCustomerPropertyValue(PropertyEnum.Mesomorph.toStr()).toInt();
    _endo = repository.getCustomerPropertyValue(PropertyEnum.Endomorph.toStr()).toInt();
    print("** Init ecto: " + _ecto.toString() + " mezo: " + _mezo.toString() + " endo: " + _endo.toString());
    if (_ecto > 0 && _mezo > 0 && _endo > 0) {
      calculateBodyType(init: true);
      origBodyTypeValue = bodyTypeValue;
    }
  }

  int value = 0;
  int step = 1;
  int bodyTypeValue = 0;
  int origBodyTypeValue = 0;
  int _ecto = 0;
  int _mezo = 0;
  int _endo = 0;

  @override
  Stream<BodytypeState> mapEventToState(
    BodytypeEvent event,
  ) async* {
    try {
      if (event is BodytypeClick) {
        yield BodytypeLoading();
        this.value = event.value;
        answers[step - 1] = value;
        calculateBodyType();
        await saveToDB();
        if (step >= questions.length) {
          yield BodytypeFinished();
          return;
        }
        step++;
        yield BodytypeReady();
      } else if (event is BodytypeBack) {
        yield BodytypeLoading();
        if (step == 1) {
          yield BodytypeReady();
          return;
        }
        step--;
        this.value = answers[step - 1];
        yield BodytypeReady();
      }
    } on Exception catch (e) {
      yield BodytypeError(error: e.toString());
    }
  }

  bool showResults() {
    return this.state == BodytypeFinished() || origBodyTypeValue > 0;
  }

  Future<void> saveToDB() async {
    await savePropertyDB(PropertyEnum.Ectomorph.toStr(), _ecto.toDouble());
    await savePropertyDB(PropertyEnum.Mesomorph.toStr(), _mezo.toDouble());
    await savePropertyDB(PropertyEnum.Endomorph.toStr(), _endo.toDouble());
  }

  Future<void> savePropertyDB(String name, double value) async {
    final now = DateTime.now();
    Property? property = repository.propertyRepository.getPropertyByName(name);
    if (property != null) {
      int propertyId = property.propertyId;
      CustomerProperty? customerProperty = repository.getCustomerProperty(name);
      if (customerProperty == null || customerProperty.customerPropertyId == null) {
        customerProperty =
            CustomerProperty(customerId: Cache().userLoggedIn!.customerId!, propertyId: propertyId, propertyValue: value, dateAdd: now);

        CustomerProperty newProperty = await CustomerApi().addProperty(customerProperty);
        repository.setCustomerProperty(name, value, id: newProperty.customerPropertyId);
      } else {
        customerProperty.propertyValue = value;
        customerProperty.dateAdd = now;
        await CustomerApi().updateProperty(customerProperty);
        repository.setCustomerProperty(name, value);
      }
    }
  }

  String getQuestion() {
    return questions[step - 1];
  }

  int getValue() {
    return answers[this.step - 1];
  }

  int getPrevValue() {
    return step < 2 ? 0 : answers[this.step - 2];
  }

  int getBodyTypeValue() {
    return bodyTypeValue;
  }

  void calculateBodyType({bool init = false}) {
    if (!init) {
      _endo = 0;
      _mezo = 0;
      _ecto = 0;
      for (int index = 0; index < step; index++) {
        _endo += getValueByWeight(weights[index][0], answers[index]);
        _mezo += getValueByWeight(weights[index][1], answers[index]);
        _ecto += getValueByWeight(weights[index][2], answers[index]);
      }
      print("ecto: " + _ecto.toString() + " mezo: " + _mezo.toString() + " endo: " + _endo.toString());
    }

    if (_ecto < 50) {
      bodyTypeValue = (50 + ((50 / (_mezo + _endo)) * _endo)).toInt();
    } else if (_endo < 50) {
      bodyTypeValue = (0 + ((50 / (_mezo + _ecto)) * _mezo)).toInt();
    } else {
      // random answers probably
      bodyTypeValue = (0 + ((100 / (_endo + _ecto))) * _endo).toInt();
    }
    origBodyTypeValue = 0;
    print("bodyTypeValue: " + bodyTypeValue.toString());
  }

  int getValueByWeight(int weight, int answer) {
    if (answer == 4) {
      if (weight > 3 && weight < 7) {
        return ((weight - (5 - answer) * 1.25)).round();
      } else if (weight >= 7) {
        return ((weight - (5 - answer) * 2)).round();
      } else {
        return (weight + (5 - answer) * 1.25).round();
      }
    } else if (answer == 3) {
      if (weight > 3 && weight < 7) {
        return ((weight - (5 - answer))).round();
      } else if (weight >= 7) {
        return ((weight - (5 - answer) * 2)).round();
      } else {
        return ((weight + (5 - answer) * 1.5)).round();
      }
    } else if (answer == 2) {
      if (weight > 3 && weight < 7) {
        return ((weight - (5 - answer))).round();
      } else if (weight >= 7) {
        return ((weight - (5 - answer) * 2)).round();
      } else if (weight == 3) {
        return ((weight + (5 - answer) * 0.25)).round();
      } else {
        return ((weight + (5 - answer) * 1.75)).round();
      }
    } else if (answer == 1) {
      if (weight > 3 && weight < 7) {
        return ((weight - (5 - answer))).round();
      } else if (weight >= 7) {
        return ((weight - (5 - answer) * 1.85)).round();
      } else if (weight == 3) {
        return weight;
      } else {
        return ((weight + (5 - answer) * 1.85)).round();
      }
    } else {
      return weight;
    }
  }
}
