package com.airlock.iam.factor.domain.model.airlock2fa;

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
import com.airlock.iam.base.misc.json.JsonCoding;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public final class Airlock2FAAuthTokenIdCoderImpl implements Airlock2FAAuthTokenIdCoder {
   @Override
   public String encode(Airlock2FAAuthTokenId authTokenId) {
      Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean bean = Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean.fromAuthTokenId(authTokenId);
      return JsonCoding.toJson(bean);
   }

   @Override
   public Optional<Airlock2FAAuthTokenId> decode(String encodedAuthTokenId) {
      return JsonCoding.tryFromJsonTo(encodedAuthTokenId, Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean.class)
         .flatMap(Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean::toAuthTokenId);
   }

   private static final class Airlock2FAAuthTokenIdBean {
      private static final BiMap<Airlock2FAFactor, String> FACTOR_CODING = ImmutableBiMap.builder()
         .put(Airlock2FAFactor.ONE_TOUCH, "ONE_TOUCH")
         .put(Airlock2FAFactor.ONLINE_QR_CODE, "ONLINE_QR_CODE")
         .put(Airlock2FAFactor.OFFLINE_QR_CODE, "QR_CODE")
         .put(Airlock2FAFactor.PASSCODE, "PASSCODE")
         .put(Airlock2FAFactor.MOBILE_ONLY, "MOBILE_ONLY")
         .put(Airlock2FAFactor.USERNAMELESS_QR_CODE, "USERNAMELESS_QR_CODE")
         .build();
      private String loginFactor;
      private String deviceId;
      private List<String> allowedFactors;

      private static Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean fromAuthTokenId(Airlock2FAAuthTokenId authTokenId) {
         return new Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean(
            encodeFactor(authTokenId.loginFactor()),
            authTokenId.deviceId().getId(),
            authTokenId.allowedFactors().stream().map(Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean::encodeFactor).toList()
         );
      }

      private Optional<Airlock2FAAuthTokenId> toAuthTokenId() {
         Optional<Airlock2FAFactor> decodedFactor = decodeFactor(this.loginFactor);
         List<Optional<Airlock2FAFactor>> factorSet = this.allowedFactors != null
            ? this.allowedFactors.stream().map(Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean::decodeFactor).toList()
            : Collections.emptyList();
         return !decodedFactor.isEmpty() && !factorSet.stream().anyMatch(Optional::isEmpty)
            ? Optional.of(
               new Airlock2FAAuthTokenId(
                  decodedFactor.get(),
                  Airlock2FADeviceId.airlock2FADeviceId(this.deviceId),
                  Airlock2FAFactorSet.of(factorSet.stream().flatMap(Optional::stream).toList())
               )
            )
            : Optional.empty();
      }

      private static String encodeFactor(Airlock2FAFactor factor) {
         return Objects.requireNonNull((String)FACTOR_CODING.get(factor), "Missing encoding for factor: " + factor);
      }

      private static Optional<Airlock2FAFactor> decodeFactor(String factor) {
         return Optional.ofNullable((Airlock2FAFactor)FACTOR_CODING.inverse().get(factor));
      }

      @SuppressFBWarnings(justification = "generated code")
      public String getLoginFactor() {
         return this.loginFactor;
      }

      @SuppressFBWarnings(justification = "generated code")
      public String getDeviceId() {
         return this.deviceId;
      }

      @SuppressFBWarnings(justification = "generated code")
      public List<String> getAllowedFactors() {
         return this.allowedFactors;
      }

      @SuppressFBWarnings(justification = "generated code")
      public void setLoginFactor(String loginFactor) {
         this.loginFactor = loginFactor;
      }

      @SuppressFBWarnings(justification = "generated code")
      public void setDeviceId(String deviceId) {
         this.deviceId = deviceId;
      }

      @SuppressFBWarnings(justification = "generated code")
      public void setAllowedFactors(List<String> allowedFactors) {
         this.allowedFactors = allowedFactors;
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public boolean equals(Object o) {
         if (o == this) {
            return true;
         } else if (!(o instanceof Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean other)) {
            return false;
         } else {
            Object this$loginFactor = this.loginFactor;
            Object other$loginFactor = other.loginFactor;
            if (this$loginFactor == null ? other$loginFactor == null : this$loginFactor.equals(other$loginFactor)) {
               Object this$deviceId = this.deviceId;
               Object other$deviceId = other.deviceId;
               if (this$deviceId == null ? other$deviceId == null : this$deviceId.equals(other$deviceId)) {
                  Object this$allowedFactors = this.allowedFactors;
                  Object other$allowedFactors = other.allowedFactors;
                  return this$allowedFactors == null ? other$allowedFactors == null : this$allowedFactors.equals(other$allowedFactors);
               } else {
                  return false;
               }
            } else {
               return false;
            }
         }
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public int hashCode() {
         int PRIME = 59;
         int result = 1;
         Object $loginFactor = this.loginFactor;
         result = result * 59 + ($loginFactor == null ? 43 : $loginFactor.hashCode());
         Object $deviceId = this.deviceId;
         result = result * 59 + ($deviceId == null ? 43 : $deviceId.hashCode());
         Object $allowedFactors = this.allowedFactors;
         return result * 59 + ($allowedFactors == null ? 43 : $allowedFactors.hashCode());
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Airlock2FAAuthTokenIdCoderImpl.Airlock2FAAuthTokenIdBean(loginFactor="
            + this.getLoginFactor()
            + ", deviceId="
            + this.getDeviceId()
            + ", allowedFactors="
            + this.getAllowedFactors()
            + ")";
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAuthTokenIdBean() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAuthTokenIdBean(String loginFactor, String deviceId, List<String> allowedFactors) {
         this.loginFactor = loginFactor;
         this.deviceId = deviceId;
         this.allowedFactors = allowedFactors;
      }
   }
}
