import 'dart:convert';
import 'package:aitrainer_app/util/app_language.dart';
import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:aitrainer_app/repository/user_repository.dart';
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

class DateRate {
  static String daily = "daily";
  static String weekly = "weekly";
  static String monthly = "monthly";
  static String yearly = "yearly";
}

mixin Common {
  final emailError = "Please type a right email address here.";
  final passwordError = "The password must have at least 8 characters.";

  String toJson(Map<String, String> map) {
    String rc = "{";
    map.forEach((key, value) {
      rc += "'$key':'$value'";
    });
    rc += "}";
    return rc;
  }

  ExerciseType? getExerciseType(int exerciseTypeId) {
    ExerciseType? returnElement;
    List<ExerciseType>? listExerciseType = Cache().getExerciseTypes();
    if (listExerciseType != null) {
      for (var element in listExerciseType) {
        if (exerciseTypeId == element.exerciseTypeId) {
          returnElement = element;
          break;
        }
      }
    }
    return returnElement;
  }

  String getDateLocale(DateTime datetime, bool timeDisplay) {
    var date = datetime;

    String dateName = DateFormat(DateFormat.YEAR_NUM_MONTH_DAY, AppLanguage().appLocal.toString()).format(date.toUtc());
    if (timeDisplay) {
      dateName += " " + DateFormat(DateFormat.HOUR_MINUTE, AppLanguage().appLocal.toString()).format(date.toUtc());
    }

    return dateName;
  }

  String utf8convert(String text) {
    List<int> bytes = text.toString().codeUnits;
    return utf8.decode(bytes);
  }

  double mediaSizeWidth(BuildContext context) {
    return MediaQuery.of(context).size.width;
  }

  bool validateEmail(UserRepository userRepository) {
    final String email = userRepository.user.email!;
    final RegExp _emailRegExp = RegExp(
      r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$',
    );
    return _emailRegExp.hasMatch(email);
  }

  bool validatePassword(UserRepository userRepository) {
    final password = userRepository.user.password!;
    final RegExp _passwordRegExp = RegExp(r'^(?=.*[A-Za-z0-9])(?=.*\d)[A-Za-z\d]{7,}$');

    return _passwordRegExp.hasMatch(password);
  }

  /// Calculates week number from a date as per https://en.wikipedia.org/wiki/ISO_week_date#Calculation
  int weekNumber(DateTime date) {
    int dayOfYear = int.parse(DateFormat("D").format(date));
    return ((dayOfYear - date.weekday + 10) / 7).floor();
  }

  static String? emailValidation(String? email) {
    final String error = "Please type an email address";
    if (email == null) {
      return error;
    }
    bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(email);
    return emailValid ? null : error;
  }

  static String? passwordValidation(String? value) {
    final String error = "Password too short";
    if (value == null || value.length == 0) {
      return error;
    }
    bool valid = 8 < value.length;
    return valid ? null : error;
  }

  static Widget badgedIcon(Color color, IconData icon, String badgeKey) {
    //print("BadgetIcon: " + Cache().getBadges().toString());
    int? badgeValue = Cache().getBadges()[badgeKey];
    bool show = (badgeValue != null);
    int counter = show ? badgeValue : 0;
    //print("show $show BadgeKey $badgeKey count $counter");
    return Badge(
      position: BadgePosition.topEnd(top: -10, end: -10),
      animationDuration: Duration(milliseconds: 500),
      animationType: BadgeAnimationType.slide,
      badgeColor: Colors.red,
      showBadge: show,
      badgeContent: Text(
        counter.toString(),
        style: TextStyle(color: Colors.white),
      ),
      child: Icon(
        icon,
        color: color,
      ),
    );
  }

  static normalizeDecimal(String value) {
    if (value.isEmpty) {
      return 0;
    }
    value = value.replaceFirst(",", ".");
    value = value.replaceAll(RegExp(r'[^0-9.]'), "");
    return value;
  }

  double calculate1RM(double weight, double repeat) {
    if (weight == 0 || repeat == 0) {
      return 0;
    }

    double rmWendler = weight * repeat * 0.0333 + weight;
    double rmOconner = weight * (1 + repeat / 40);
    //print("Weight: $weight repeat: $repeat,  $rmWendler, Oconner: $rmOconner");
    double average = (rmWendler + rmOconner) / 2;

    return average;
  }

  static double get1RMPercent(int repeats) {
    double percent = 1;

    if (repeats >= 35) {
      percent = 0.50;
    } else if (repeats > 12) {
      percent = (100 - 2 * repeats) / 100;
    } else {
      percent = (100.0 - 2 * repeats) / 100;
    }
    //print("1RM Percent: $percent repeats: $repeats");
    return percent;
  }

  static double roundWeight(double weight) {
    double rounded = weight.round().toDouble();

    if (weight > 35) {
      final double remainder = weight % 5;
      if (remainder < 1) {
        rounded = ((weight / 5).floor() * 5).toDouble();
      } else if (remainder > 1 && remainder <= 2.5) {
        rounded = (weight / 5).floor() * 5 + 2.5;
      } else if (remainder > 2.5 && remainder < 3.25) {
        rounded = (weight / 5).floor() * 5 + 2.5;
      } else {
        rounded = (((weight / 5).ceil() * 5)).toDouble();
      }
    }

    return rounded;
  }

  static int calculateQuantityByChangedWeight(double initialRM, double weight, double repeat) {
    final double repeatWendler = (initialRM - weight) / 0.0333 / weight;
    final double repeatOconner = (initialRM / weight - 1) * 40;
    final newRepeat = ((repeatOconner + repeatWendler) / 2).ceil();
    print("Initial 1RM: $initialRM Weight: $weight repeatWendler: $repeatWendler repeat Oconner: $repeatOconner. NEW REPEAT: $newRepeat");
    return newRepeat;
  }

  static int reCalculateRepeatsByChangedWeight(double weight, double repeat, double changedWeight) {
    final double rmWendler = weight * repeat * 0.0333 + weight;
    final double rmOconner = weight * (1 + repeat / 40);

    final double repeatWendler = (rmWendler - changedWeight) / 0.0333 / changedWeight;
    final double repeatOconner = (rmOconner / changedWeight - 1) * 40;
    final newRepeat = ((repeatOconner + repeatWendler) / 2).ceil();
    print("Weight: $weight changedWeight: $changedWeight repeatWendler: $repeatWendler repeat Oconner: $repeatOconner. NEW REPEAT: $newRepeat");
    return newRepeat;
  }

  static double calculateWeigthByChangedQuantity(double weight, double repeat, double changedRepeats) {
    final double rmWendler = weight * repeat * 0.0333 + weight;
    final double rmOconner = weight * (1 + repeat / 40);
    final double initialRM = (rmWendler + rmOconner) / 2;

    final double weightWendler = rmWendler / (changedRepeats * 0.0333 + 1);
    final double weightOconner = rmOconner / (1 + changedRepeats / 40);
    final double newWeight = ((weightWendler + weightOconner) / 2);
    print(
        "Initial 1RM: $initialRM repeat: $repeat changedRepeat: $changedRepeats Weight: $weight weightWendler: $weightWendler weight Oconner: $weightOconner. NEW WEIGHT: $newWeight");
    return newWeight;
  }

  static List<ExerciseType> getExerciseTypeAlternatives(int? exerciseTypeId) {
    if (exerciseTypeId == null || exerciseTypeId <= 0) {
      return [];
    }
    List<ExerciseType> list = [];
    List<ExerciseType>? exerciseTypes = Cache().getExerciseTypes();
    if (exerciseTypes != null) {
      exerciseTypes.forEach((exerciseType) {
        if (exerciseType.alternatives.isNotEmpty) {
          exerciseType.alternatives.forEach((childId) {
            if (childId == exerciseTypeId) {
              list.add(exerciseType);
            }
          });
        }
      });
    }

    return list;
  }
}