package com.airlock.iam.common.domain.model.user.profileitems;

import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.plugin.framework.domain.model.property.EnumPropertyTranslator;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nonnull;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;

public class InternationalPhoneNumberProfileItemLogic implements UserProfileItemLogic {
   protected static final ELogger LOG = ELoggerFactory.getLogger(InternationalPhoneNumberProfileItemLogic.class);
   @Nonnull
   private final InternationalPhoneNumberProfileItemLogic.ValidationLevel validationLevel;
   private final boolean validateOnlyChangedValues;
   private final boolean optional;
   private final boolean modifiable;
   private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();

   public static InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder builder(
      InternationalPhoneNumberProfileItemLogic.ValidationLevel validationLevel
   ) {
      return new InternationalPhoneNumberProfileItemLogic(validationLevel, false, false, true).toBuilder();
   }

   @Override
   public UserProfileItemLogic.CheckResult validate(Object newValue, Object oldValue, String uid) {
      if (!UserProfileItemLogic.validationRequired(newValue, oldValue, this.validateOnlyChangedValues)) {
         return UserProfileItemLogic.CheckResult.IS_OK;
      } else {
         String phoneNumber = Objects.toString(newValue, "");
         if (StringUtils.isBlank(phoneNumber)) {
            return this.optional ? UserProfileItemLogic.CheckResult.IS_OK : UserProfileItemLogic.CheckResult.REQUIRED;
         } else {
            return this.validatePhoneNumber(phoneNumber);
         }
      }
   }

   @Override
   public Object formatForPersistence(Object value) {
      String phoneNumberString = StringUtils.trimToEmpty(Objects.toString(value, ""));
      if (StringUtils.isEmpty(phoneNumberString)) {
         return "";
      }

      Optional<PhoneNumber> phoneNumber = this.parsePhoneNumberInInternationalFormat(phoneNumberString);
      return phoneNumber.isPresent() ? this.phoneUtil.format(phoneNumber.get(), PhoneNumberFormat.E164) : phoneNumberString;
   }

   @Override
   public Object transformPersistentValue(Object value) {
      return Objects.toString(value, null);
   }

   @Override
   public boolean isModifiable(Object value) {
      return this.modifiable;
   }

   private Optional<PhoneNumber> parsePhoneNumberInInternationalFormat(String phoneNumber) {
      try {
         return Optional.of(this.phoneUtil.parse(phoneNumber, null));
      } catch (NumberParseException e) {
         LOG.debug("Failed to parse phone number " + LogUtil.logSafeQuote(phoneNumber) + ": " + e.getMessage());
         return Optional.empty();
      }
   }

   private UserProfileItemLogic.CheckResult validatePhoneNumber(String phoneNumber) {
      Optional<PhoneNumber> parsedPhoneNumber = this.parsePhoneNumberInInternationalFormat(phoneNumber);
      if (parsedPhoneNumber.isEmpty()) {
         return UserProfileItemLogic.CheckResult.WRONG_FORMAT;
      }

      if (InternationalPhoneNumberProfileItemLogic.ValidationLevel.MINIMAL.equals(this.validationLevel)) {
         return UserProfileItemLogic.CheckResult.IS_OK;
      }

      if (InternationalPhoneNumberProfileItemLogic.ValidationLevel.LENIENT.equals(this.validationLevel)) {
         if (this.phoneUtil.isPossibleNumber(parsedPhoneNumber.get())) {
            return UserProfileItemLogic.CheckResult.IS_OK;
         }

         LOG.debug(phoneNumber + " is not a possible phone number.");
         return UserProfileItemLogic.CheckResult.WRONG_FORMAT;
      } else {
         if (this.phoneUtil.isValidNumber(parsedPhoneNumber.get())) {
            return UserProfileItemLogic.CheckResult.IS_OK;
         }

         LOG.debug(phoneNumber + " is not a valid phone number.");
         return UserProfileItemLogic.CheckResult.WRONG_FORMAT;
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder toBuilder() {
      return new InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder()
         .validationLevel(this.validationLevel)
         .validateOnlyChangedValues(this.validateOnlyChangedValues)
         .optional(this.optional)
         .modifiable(this.modifiable);
   }

   @SuppressFBWarnings(justification = "generated code")
   public InternationalPhoneNumberProfileItemLogic(
      @Nonnull InternationalPhoneNumberProfileItemLogic.ValidationLevel validationLevel,
      boolean validateOnlyChangedValues,
      boolean optional,
      boolean modifiable
   ) {
      if (validationLevel == null) {
         throw new NullPointerException("validationLevel is marked non-null but is null");
      }

      this.validationLevel = validationLevel;
      this.validateOnlyChangedValues = validateOnlyChangedValues;
      this.optional = optional;
      this.modifiable = modifiable;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class InternationalPhoneNumberProfileItemLogicBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private InternationalPhoneNumberProfileItemLogic.ValidationLevel validationLevel;
      @SuppressFBWarnings(justification = "generated code")
      private boolean validateOnlyChangedValues;
      @SuppressFBWarnings(justification = "generated code")
      private boolean optional;
      @SuppressFBWarnings(justification = "generated code")
      private boolean modifiable;

      @SuppressFBWarnings(justification = "generated code")
      InternationalPhoneNumberProfileItemLogicBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder validationLevel(
         @Nonnull InternationalPhoneNumberProfileItemLogic.ValidationLevel validationLevel
      ) {
         if (validationLevel == null) {
            throw new NullPointerException("validationLevel is marked non-null but is null");
         }

         this.validationLevel = validationLevel;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder validateOnlyChangedValues(
         boolean validateOnlyChangedValues
      ) {
         this.validateOnlyChangedValues = validateOnlyChangedValues;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder optional(boolean optional) {
         this.optional = optional;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder modifiable(boolean modifiable) {
         this.modifiable = modifiable;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public InternationalPhoneNumberProfileItemLogic build() {
         return new InternationalPhoneNumberProfileItemLogic(this.validationLevel, this.validateOnlyChangedValues, this.optional, this.modifiable);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "InternationalPhoneNumberProfileItemLogic.InternationalPhoneNumberProfileItemLogicBuilder(validationLevel="
            + this.validationLevel
            + ", validateOnlyChangedValues="
            + this.validateOnlyChangedValues
            + ", optional="
            + this.optional
            + ", modifiable="
            + this.modifiable
            + ")";
      }
   }

   public enum ValidationLevel implements EnumPropertyTranslator {
      DEFAULT("Default (Length and prefix)"),
      LENIENT("Lenient (Length only)"),
      MINIMAL("Minimal");

      private final String displayName;

      ValidationLevel(String displayName) {
         this.displayName = displayName;
      }

      public String getDisplayValue() {
         return this.displayName;
      }
   }
}
