import 'dart:collection';

import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/customer.dart';
import 'package:aitrainer_app/model/customer_property.dart';
import 'package:aitrainer_app/model/product_test.dart';
import 'package:aitrainer_app/model/property.dart';
import 'package:aitrainer_app/model/purchase.dart';
import 'package:aitrainer_app/model/sport.dart';
import 'package:aitrainer_app/repository/property_repository.dart';
import 'package:aitrainer_app/service/customer_service.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/service/product_test_service.dart';
import 'package:aitrainer_app/service/purchase_service.dart';
import 'package:aitrainer_app/util/enums.dart';
import 'package:aitrainer_app/util/not_found_exception.dart';

class GenderItem {
  GenderItem(this.dbValue, this.name);
  final String dbValue;
  String name;
}

class CustomerRepository with Logging {
  Customer? customer;
  Customer? _trainee;
  List<Customer>? _trainees;
  List<CustomerProperty>? _allProperties;
  final PropertyRepository propertyRepository = PropertyRepository();
  final List<Property> womanSizes = [];
  final List<Property> manSizes = [];

  final double baseWidth = 312;
  final double baseHeight = 675.2;
  double mediaWidth = 0;
  double mediaHeight = 0;
  bool isMan = true;

  //List<CustomerRepository> customerList = List<CustomerRepository>();
  bool visibleDetails = false;
  late List<GenderItem> genders;

  CustomerRepository() {
    customer = Customer();

    if (Cache().userLoggedIn != null) {
      isMan = (Cache().userLoggedIn!.sex == "m");
    }
  }

  String? getGenderByName(String name) {
    String? dbValue;
    genders.forEach((element) {
      if (element.name == name) {
        dbValue = element.dbValue;
      }
    });
    return dbValue;
  }

  String? getGenderByDBValue(String dbValue) {
    String? name;
    genders.forEach((element) {
      if (element.dbValue == dbValue) {
        name = element.name;
      }
    });
    return name;
  }

  String? get name {
    return this.customer != null && this.customer!.name != null ? this.customer!.name : "";
  }

  String? get firstName {
    return this.customer != null && this.customer!.firstname != null ? this.customer!.firstname : "";
  }

  String get sex {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!.sex == "m" ? "Man" : "Woman";
  }

  int? get birthYear {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!.birthYear;
  }

  String? get goal {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!.goal;
  }

  String? getSportString() {
    if (this.customer == null) throw Exception("Initialize the customer object");
    String? sport;
    List<Sport>? sports = Cache().getSports();
    if (sports != null) {
      for (Sport sportObject in sports) {
        if (sportObject.sportId == this.customer!.sportId) {
          sport = sportObject.name;
          break;
        }
      }
    }
    return sport;
  }

  Sport? getSport() {
    if (this.customer == null) throw Exception("Initialize the customer object");
    Sport? sport;
    List<Sport>? sports = Cache().getSports();
    if (sports != null) {
      for (Sport sportObject in sports) {
        if (sportObject.sportId == this.customer!.sportId) {
          sport = sportObject;
          break;
        }
      }
    }
    return sport;
  }

  String? get fitnessLevel {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!.fitnessLevel;
  }

  String? get bodyType {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!.bodyType;
  }

  setName(String name) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.name = name;
  }

  setFirstName(String firstName) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.firstname = firstName;
  }

  setPassword(String password) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.password = password;
  }

  setEmail(String email) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.email = email;
  }

  setSex(String sex) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.sex = sex;
  }

  setWeight(double weight) {
    final propertyName = "Weight";
    this.setCustomerProperty(propertyName, weight);
  }

  setHeight(int height) {
    final propertyName = "Height";
    this.setCustomerProperty(propertyName, height.toDouble());
  }

  setCustomerProperty(String propertyName, double value, {id = 0}) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    if (this.customer!.properties[propertyName] == null) {
      this.customer!.properties[propertyName] = CustomerProperty(
          propertyId: propertyRepository.getPropertyByName("Height")!.propertyId,
          customerId: this.customer!.customerId!,
          propertyValue: value,
          dateAdd: DateTime.now());
    } else {
      this.customer!.properties[propertyName]!.propertyValue = value;
    }
    this.customer!.properties[propertyName]!.dateAdd = DateTime.now();
    this.customer!.properties[propertyName]!.newData = true;
    if (id > 0) {
      this.customer!.properties[propertyName]!.customerPropertyId = id;
    }
  }

  double getWeight() {
    return getCustomerPropertyValue("Weight");
  }

  double getHeight() {
    return getCustomerPropertyValue("Height");
  }

  double getCustomerPropertyValue(String propertyName) {
    if (this.customer == null || this.customer!.properties[propertyName] == null) {
      return 0.0;
    } else {
      return this.customer!.properties[propertyName]!.propertyValue;
    }
  }

  CustomerProperty? getCustomerProperty(String propertyName) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!.properties[propertyName];
  }

  setBirthYear(int birthYear) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.birthYear = birthYear;
  }

  setFitnessLevel(String level) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.fitnessLevel = level;
  }

  setGoal(String goal) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.goal = goal;
  }

  setSportString(String selectedSport) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    List<Sport>? sports = Cache().getSports();
    if (sports != null) {
      for (Sport sportObject in sports) {
        if (sportObject.name == selectedSport) {
          this.customer!.sportId = sportObject.sportId;
          break;
        }
      }
    }
  }

  setBodyType(String bodyType) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    this.customer!.bodyType = bodyType;
  }

  createNew() {
    this.customer = Customer();
  }

  Customer getCustomer() {
    if (this.customer == null) throw Exception("Initialize the customer object");
    return this.customer!;
  }

  void setCustomer(Customer customer) {
    this.customer = customer;
  }

  Future<void> addCustomer() async {
    if (this.customer == null) throw Exception("Initialize the customer object");
    final Customer modelCustomer = customer!;
    await CustomerApi().addCustomer(modelCustomer);
  }

  Future<void> saveCustomer() async {
    if (this.customer == null) throw Exception("Initialize the customer object");
    final Customer modelCustomer = customer!;
    await CustomerApi().saveCustomer(modelCustomer);
    await this.saveProperties(modelCustomer.properties);
  }

  Future<void> saveProperties(LinkedHashMap<String, CustomerProperty> properties) async {
    properties.forEach((propertyName, property) async {
      if (property.newData == true) {
        await CustomerApi().addProperty(property);
        property.newData = false;
      }
    });
  }

  Future<void> savePropertyByName(String name) async {
    await Future.forEach(this._allProperties!, (element) async {
      final CustomerProperty customerProperty = element as CustomerProperty;
      final Property? property = propertyRepository.getPropertyByName(name);
      if (property != null) {
        if (property.propertyId == customerProperty.propertyId) {
          await CustomerApi().updateProperty(customerProperty);
        }
      }
    });
  }

  Future<Customer?> getTraineeAsCustomer() async {
    this._trainee = await CustomerApi().getTrainee(Cache().userLoggedIn!.customerId!);
    return _trainee;
  }

  Future<List<Customer?>?> getTrainees() async {
    int trainerId = Cache().userLoggedIn!.customerId!;
    final results = await CustomerApi().getTrainees(trainerId);
    this._trainees = results;
    return results;
  }

  Future<List<CustomerProperty>> getAllCustomerProperties() async {
    int customerId = Cache().userLoggedIn!.customerId!;
    final results = await CustomerApi().getAllProperties(customerId);
    this._allProperties = results;
    return results;
  }

  List<CustomerProperty>? getAllProperties() {
    return this._allProperties;
  }

  List<Customer>? getTraineesList() {
    return _trainees;
  }

  void setTrainee(int traineeId) {
    if (_trainees == null) {
      return;
    }
    _trainees!.forEach((element) {
      if (traineeId == element.customerId) {
        this._trainee = element;
      }
    });
  }

  void emptyTrainees() {
    _trainees = null;
    _trainee = null;
  }

  Customer? getTrainee() {
    return this._trainee;
  }

  Customer? getTraineeById(int customerId) {
    if (_trainees == null) {
      return null;
    }
    _trainees!.forEach((element) {
      if (customerId == element.customerId) {
        this._trainee = element;
      }
    });
    return _trainee;
  }

  Future<List<Purchase>> getPurchase() async {
    int customerId = Cache().userLoggedIn!.customerId!;
    List<Purchase> purchases = await PurchaseApi().getPurchasesByCustomer(customerId);
    return purchases;
  }

  Future<void> addPurchase(Purchase purchase) async {
    await PurchaseApi().savePurchase(purchase);
  }

  Future<List<ProductTest>> getProductTests() async {
    List<ProductTest> tests = [];
    try {
      int customerId = Cache().userLoggedIn!.customerId!;
      tests = await ProductTestApi().getProductTestByCustomer(customerId);
    } on NotFoundException catch (_) {
      log("Product Tests not found");
      Cache().productTests = tests;
    } on Exception catch (ex) {
      log(ex.toString());
      Cache().productTests = tests;
    }
    return tests;
  }

  Future<void> addProductTest(ProductTest productTest) async {
    await ProductTestApi().saveProductTest(productTest);
  }

  void setMediaDimensions(double width, double height) {
    this.mediaHeight = height;
    this.mediaWidth = width;
    this.addSizes(this.sex);
  }

  void addSizes(String sex) {
    if (this.customer == null) throw Exception("Initialize the customer object");
    List<Property>? properties = Cache().getProperties();
    if (properties == null) {
      return;
    }
    final double distortionWidth = mediaWidth / baseWidth;
    final double distortionHeight = mediaHeight / baseHeight;
    if (isMan) {
      properties.forEach((element) {
        if (element.propertyName == "Shoulder") {
          element.top = (122 * distortionHeight).toInt();
          element.left = (130 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Shoulder");
          manSizes.add(element);
        } else if (element.propertyName == "Neck") {
          element.top = (68 * distortionHeight).toInt();
          element.left = (130 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Neck");
          manSizes.add(element);
        } else if (element.propertyName == "Biceps") {
          element.top = (178 * distortionHeight).toInt();
          element.left = (208 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Biceps");
          manSizes.add(element);
        } else if (element.propertyName == "Chest") {
          element.top = (154 * distortionHeight).toInt();
          element.left = (130 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Chest");
          manSizes.add(element);
        } else if (element.propertyName == "Belly") {
          element.top = (244 * distortionHeight).toInt();
          element.left = (130 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Belly");
          manSizes.add(element);
        } else if (element.propertyName == "Hip") {
          element.top = (308 * distortionHeight).toInt();
          element.left = (130 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Hip");
          manSizes.add(element);
        } else if (element.propertyName == "Thigh Top") {
          element.top = (332 * distortionHeight).toInt();
          element.left = (165 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Thigh Top");
          manSizes.add(element);
        } else if (element.propertyName == "Thigh Middle") {
          element.top = (382 * distortionHeight).toInt();
          element.left = (100 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Thigh Middle");
          manSizes.add(element);
        } else if (element.propertyName == "Knee") {
          element.top = (464 * distortionHeight).toInt();
          element.left = (97 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Knee");
          manSizes.add(element);
        } else if (element.propertyName == "Calf") {
          element.top = (520 * distortionHeight).toInt();
          element.left = (97 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Calf");
          manSizes.add(element);
        } else if (element.propertyName == "Ankle") {
          element.top = (620 * distortionHeight).toInt();
          element.left = (150 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Ankle");
          manSizes.add(element);
        } else if (element.propertyName == "Weight") {
          element.top = (402 * distortionHeight).toInt();
          element.left = (240 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Weight");
          manSizes.add(element);
        }
      });
    } else {
      properties.forEach((element) {
        if (element.propertyName == "Shoulder") {
          element.top = (122 * distortionHeight).toInt();
          element.left = (151 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Shoulder");
          manSizes.add(element);
        } else if (element.propertyName == "Neck") {
          element.top = (78 * distortionHeight).toInt();
          element.left = (151 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Neck");
          manSizes.add(element);
        } else if (element.propertyName == "Biceps") {
          element.top = (178 * distortionHeight).toInt();
          element.left = (212 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Biceps");
          manSizes.add(element);
        } else if (element.propertyName == "Chest") {
          element.top = (154 * distortionHeight).toInt();
          element.left = (151 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Chest");
          manSizes.add(element);
        } else if (element.propertyName == "Belly") {
          element.top = (230 * distortionHeight).toInt();
          element.left = (151 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Belly");
          manSizes.add(element);
        } else if (element.propertyName == "Hip") {
          element.top = (294 * distortionHeight).toInt();
          element.left = (151 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Hip");
          manSizes.add(element);
        } else if (element.propertyName == "Thigh Top") {
          element.top = (335 * distortionHeight).toInt();
          element.left = (185 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Thigh Top");
          manSizes.add(element);
        } else if (element.propertyName == "Thigh Middle") {
          element.top = (377 * distortionHeight).toInt();
          element.left = (125 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Thigh Middle");
          manSizes.add(element);
        } else if (element.propertyName == "Knee") {
          element.top = (468 * distortionHeight).toInt();
          element.left = (129 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Knee");
          manSizes.add(element);
        } else if (element.propertyName == "Calf") {
          element.top = (525 * distortionHeight).toInt();
          element.left = (129 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Calf");
          manSizes.add(element);
        } else if (element.propertyName == "Ankle") {
          element.top = (620 * distortionHeight).toInt();
          element.left = (162 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Ankle");
          manSizes.add(element);
        } else if (element.propertyName == "Weight") {
          element.top = (402 * distortionHeight).toInt();
          element.left = (240 * distortionWidth).toInt();
          element.value = this.customer!.getProperty("Weight");
          manSizes.add(element);
        }
      });
    }
  }

  int? getWeightCoordinate(isMan, {isTop = false, isLeft = false}) {
    int? value = 0;
    this.manSizes.forEach((element) {
      if (element.propertyName == SizesEnum.Weight.toStr()) {
        if (isTop == true) {
          value = element.top;
        } else if (isLeft == true) {
          value = element.left;
        }
      }
    });
    return value;
  }

  Property? getPropertyByName(String propertyName) {
    Property? property;
    List<Property> sizes;
    if (this.sex == "m") {
      sizes = this.manSizes;
    } else {
      sizes = this.womanSizes;
    }

    sizes.forEach((element) {
      if (element.propertyName == propertyName) {
        property = element;
      }
    });
    return property;
  }

  void updateSizes(String propertyName, double value) {
    List<Property> sizes;
    if (this.sex == "m") {
      sizes = this.manSizes;
    } else {
      sizes = this.womanSizes;
    }

    sizes.forEach((element) {
      if (element.propertyName == propertyName) {
        element.value = value;
      }
    });
  }
}
