package com.airlock.iam.saml2.domain.model.session;

import com.airlock.iam.base.api.domain.model.mapification.Mapifiable;
import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.domain.model.mapification.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.user.UsernameToPropagate;
import com.airlock.iam.saml2.domain.model.Saml2RelayState;
import com.airlock.iam.saml2.domain.model.assertion.FlowSaml2Attribute;
import com.airlock.iam.saml2.domain.model.idp.Saml2AuthenticationContextClassRef;
import com.airlock.iam.saml2.domain.model.idp.Saml2IdpEntityId;
import com.airlock.iam.saml2.domain.model.idp.Saml2IdpSsoContinuationId;
import com.airlock.iam.saml2.domain.model.idp.Saml2RequestId;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpEntityId;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import lombok.NonNull;

public final class Saml2PropagationAttributes implements Mapifiable {
   private static final String CONTINUATION_ID_KEY = "continuationId";
   private static final String SP_ENTITY_ID_KEY = "spEntityId";
   private static final String IDP_ENTITY_ID_KEY = "idpEntityId";
   private static final String EXP_INSTANT_KEY = "expInstant";
   private static final String USERNAME_TO_PROP_KEY = "usernameToProp";
   private static final String AUTHN_INSTANT_KEY = "authnInstant";
   private static final String ASSERTION_ATTRS_KEY = "assertionAttrs";
   private static final String AUTHN_CONTEXT_KEY = "authnContext";
   private static final String RELAY_STATE_KEY = "relayState";
   private static final String SP_INIT_REQUEST_ID_KEY = "spInitRequestId";
   @NonNull
   private final Saml2IdpSsoContinuationId ssoContinuationId;
   @NonNull
   private final Saml2SpEntityId spEntityId;
   @NonNull
   private final Saml2IdpEntityId idpEntityId;
   @NonNull
   private final Instant expiryInstant;
   @NonNull
   private final UsernameToPropagate usernameToPropagate;
   @NonNull
   private final Instant authenticationInstant;
   @NonNull
   private final List<FlowSaml2Attribute> assertionAttributes;
   @Nullable
   private final Saml2AuthenticationContextClassRef authenticationContext;
   @Nullable
   private final Saml2RelayState relayState;
   @Nullable
   private final Saml2RequestId spInitiatedRequestId;

   public Optional<Saml2AuthenticationContextClassRef> getAuthenticationContext() {
      return Optional.ofNullable(this.authenticationContext);
   }

   public Optional<Saml2RelayState> getRelayState() {
      return Optional.ofNullable(this.relayState);
   }

   public Optional<Saml2RequestId> getSpInitiatedRequestId() {
      return Optional.ofNullable(this.spInitiatedRequestId);
   }

   public boolean isExpired(Instant now) {
      return now.isAfter(this.expiryInstant);
   }

   @Override
   public String toString() {
      return String.format(
         "SAML2 Propagation Attributes [SP: %s, IDP: %s, propagated username: %s]",
         LogUtil.logSafeQuote(this.spEntityId),
         LogUtil.logSafeQuote(this.idpEntityId),
         LogUtil.logSafeQuote(this.usernameToPropagate)
      );
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("continuationId", this.ssoContinuationId.value())
         .put("spEntityId", this.spEntityId.value())
         .put("idpEntityId", this.idpEntityId.value())
         .put("expInstant", MapificationUtils.mapifyInstant(this.expiryInstant))
         .put("usernameToProp", this.usernameToPropagate.value())
         .put("authnInstant", MapificationUtils.mapifyInstant(this.authenticationInstant))
         .putIfNotEmpty("assertionAttrs", this.assertionAttributes.stream().map(FlowSaml2Attribute::mapify).toList())
         .putIfPresent("authnContext", Optional.ofNullable(this.authenticationContext).map(Saml2AuthenticationContextClassRef::value))
         .putIfPresent("relayState", Optional.ofNullable(this.relayState).map(Saml2RelayState::value))
         .putIfPresent("spInitRequestId", Optional.ofNullable(this.spInitiatedRequestId).map(Saml2RequestId::value))
         .build();
   }

   public static Saml2PropagationAttributes demapify(MapifiedValues values) {
      return new Saml2PropagationAttributes(
         values.simpleValue("continuationId", Saml2IdpSsoContinuationId::parseSsoContinuationId),
         values.simpleValue("spEntityId", Saml2SpEntityId::spEntityId),
         values.simpleValue("idpEntityId", Saml2IdpEntityId::idpEntityId),
         values.demapifyValue("expInstant", MapificationUtils::demapifyInstant),
         values.simpleValue("usernameToProp", UsernameToPropagate::usernameToPropagateOf),
         values.demapifyValue("authnInstant", MapificationUtils::demapifyInstant),
         values.demapifyList("assertionAttrs", FlowSaml2Attribute::demapify),
         values.transformOptional("authnContext", Saml2AuthenticationContextClassRef::authenticationContext).orElse(null),
         values.<String, Saml2RelayState>transformOptional("relayState", Saml2RelayState::relayState).orElse(null),
         values.transformOptional("spInitRequestId", Saml2RequestId::requestId).orElse(null)
      );
   }

   @SuppressFBWarnings(justification = "generated code")
   Saml2PropagationAttributes(
      @NonNull Saml2IdpSsoContinuationId ssoContinuationId,
      @NonNull Saml2SpEntityId spEntityId,
      @NonNull Saml2IdpEntityId idpEntityId,
      @NonNull Instant expiryInstant,
      @NonNull UsernameToPropagate usernameToPropagate,
      @NonNull Instant authenticationInstant,
      @NonNull List<FlowSaml2Attribute> assertionAttributes,
      @Nullable Saml2AuthenticationContextClassRef authenticationContext,
      @Nullable Saml2RelayState relayState,
      @Nullable Saml2RequestId spInitiatedRequestId
   ) {
      if (ssoContinuationId == null) {
         throw new NullPointerException("ssoContinuationId is marked non-null but is null");
      }

      if (spEntityId == null) {
         throw new NullPointerException("spEntityId is marked non-null but is null");
      }

      if (idpEntityId == null) {
         throw new NullPointerException("idpEntityId is marked non-null but is null");
      }

      if (expiryInstant == null) {
         throw new NullPointerException("expiryInstant is marked non-null but is null");
      }

      if (usernameToPropagate == null) {
         throw new NullPointerException("usernameToPropagate is marked non-null but is null");
      }

      if (authenticationInstant == null) {
         throw new NullPointerException("authenticationInstant is marked non-null but is null");
      }

      if (assertionAttributes == null) {
         throw new NullPointerException("assertionAttributes is marked non-null but is null");
      }

      this.ssoContinuationId = ssoContinuationId;
      this.spEntityId = spEntityId;
      this.idpEntityId = idpEntityId;
      this.expiryInstant = expiryInstant;
      this.usernameToPropagate = usernameToPropagate;
      this.authenticationInstant = authenticationInstant;
      this.assertionAttributes = assertionAttributes;
      this.authenticationContext = authenticationContext;
      this.relayState = relayState;
      this.spInitiatedRequestId = spInitiatedRequestId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static Saml2PropagationAttributes.Saml2PropagationAttributesBuilder builder() {
      return new Saml2PropagationAttributes.Saml2PropagationAttributesBuilder();
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Saml2IdpSsoContinuationId getSsoContinuationId() {
      return this.ssoContinuationId;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Saml2SpEntityId getSpEntityId() {
      return this.spEntityId;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Saml2IdpEntityId getIdpEntityId() {
      return this.idpEntityId;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Instant getExpiryInstant() {
      return this.expiryInstant;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public UsernameToPropagate getUsernameToPropagate() {
      return this.usernameToPropagate;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Instant getAuthenticationInstant() {
      return this.authenticationInstant;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public List<FlowSaml2Attribute> getAssertionAttributes() {
      return this.assertionAttributes;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof Saml2PropagationAttributes other)) {
         return false;
      } else {
         Object this$ssoContinuationId = this.ssoContinuationId;
         Object other$ssoContinuationId = other.ssoContinuationId;
         if (this$ssoContinuationId == null ? other$ssoContinuationId == null : this$ssoContinuationId.equals(other$ssoContinuationId)) {
            Object this$spEntityId = this.spEntityId;
            Object other$spEntityId = other.spEntityId;
            if (this$spEntityId == null ? other$spEntityId == null : this$spEntityId.equals(other$spEntityId)) {
               Object this$idpEntityId = this.idpEntityId;
               Object other$idpEntityId = other.idpEntityId;
               if (this$idpEntityId == null ? other$idpEntityId == null : this$idpEntityId.equals(other$idpEntityId)) {
                  Object this$expiryInstant = this.expiryInstant;
                  Object other$expiryInstant = other.expiryInstant;
                  if (this$expiryInstant == null ? other$expiryInstant == null : this$expiryInstant.equals(other$expiryInstant)) {
                     Object this$usernameToPropagate = this.usernameToPropagate;
                     Object other$usernameToPropagate = other.usernameToPropagate;
                     if (this$usernameToPropagate == null ? other$usernameToPropagate == null : this$usernameToPropagate.equals(other$usernameToPropagate)) {
                        Object this$authenticationInstant = this.authenticationInstant;
                        Object other$authenticationInstant = other.authenticationInstant;
                        if (this$authenticationInstant == null
                           ? other$authenticationInstant == null
                           : this$authenticationInstant.equals(other$authenticationInstant)) {
                           Object this$assertionAttributes = this.assertionAttributes;
                           Object other$assertionAttributes = other.assertionAttributes;
                           if (this$assertionAttributes == null
                              ? other$assertionAttributes == null
                              : this$assertionAttributes.equals(other$assertionAttributes)) {
                              Object this$authenticationContext = this.authenticationContext;
                              Object other$authenticationContext = other.authenticationContext;
                              if (this$authenticationContext == null
                                 ? other$authenticationContext == null
                                 : this$authenticationContext.equals(other$authenticationContext)) {
                                 Object this$relayState = this.relayState;
                                 Object other$relayState = other.relayState;
                                 if (this$relayState == null ? other$relayState == null : this$relayState.equals(other$relayState)) {
                                    Object this$spInitiatedRequestId = this.spInitiatedRequestId;
                                    Object other$spInitiatedRequestId = other.spInitiatedRequestId;
                                    return this$spInitiatedRequestId == null
                                       ? other$spInitiatedRequestId == null
                                       : this$spInitiatedRequestId.equals(other$spInitiatedRequestId);
                                 } else {
                                    return false;
                                 }
                              } else {
                                 return false;
                              }
                           } else {
                              return false;
                           }
                        } else {
                           return false;
                        }
                     } else {
                        return false;
                     }
                  } else {
                     return false;
                  }
               } else {
                  return false;
               }
            } else {
               return false;
            }
         } else {
            return false;
         }
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      Object $ssoContinuationId = this.ssoContinuationId;
      result = result * 59 + ($ssoContinuationId == null ? 43 : $ssoContinuationId.hashCode());
      Object $spEntityId = this.spEntityId;
      result = result * 59 + ($spEntityId == null ? 43 : $spEntityId.hashCode());
      Object $idpEntityId = this.idpEntityId;
      result = result * 59 + ($idpEntityId == null ? 43 : $idpEntityId.hashCode());
      Object $expiryInstant = this.expiryInstant;
      result = result * 59 + ($expiryInstant == null ? 43 : $expiryInstant.hashCode());
      Object $usernameToPropagate = this.usernameToPropagate;
      result = result * 59 + ($usernameToPropagate == null ? 43 : $usernameToPropagate.hashCode());
      Object $authenticationInstant = this.authenticationInstant;
      result = result * 59 + ($authenticationInstant == null ? 43 : $authenticationInstant.hashCode());
      Object $assertionAttributes = this.assertionAttributes;
      result = result * 59 + ($assertionAttributes == null ? 43 : $assertionAttributes.hashCode());
      Object $authenticationContext = this.authenticationContext;
      result = result * 59 + ($authenticationContext == null ? 43 : $authenticationContext.hashCode());
      Object $relayState = this.relayState;
      result = result * 59 + ($relayState == null ? 43 : $relayState.hashCode());
      Object $spInitiatedRequestId = this.spInitiatedRequestId;
      return result * 59 + ($spInitiatedRequestId == null ? 43 : $spInitiatedRequestId.hashCode());
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class Saml2PropagationAttributesBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Saml2IdpSsoContinuationId ssoContinuationId;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2SpEntityId spEntityId;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2IdpEntityId idpEntityId;
      @SuppressFBWarnings(justification = "generated code")
      private Instant expiryInstant;
      @SuppressFBWarnings(justification = "generated code")
      private UsernameToPropagate usernameToPropagate;
      @SuppressFBWarnings(justification = "generated code")
      private Instant authenticationInstant;
      @SuppressFBWarnings(justification = "generated code")
      private List<FlowSaml2Attribute> assertionAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2AuthenticationContextClassRef authenticationContext;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2RelayState relayState;
      @SuppressFBWarnings(justification = "generated code")
      private Saml2RequestId spInitiatedRequestId;

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

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder ssoContinuationId(@NonNull Saml2IdpSsoContinuationId ssoContinuationId) {
         if (ssoContinuationId == null) {
            throw new NullPointerException("ssoContinuationId is marked non-null but is null");
         }

         this.ssoContinuationId = ssoContinuationId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder spEntityId(@NonNull Saml2SpEntityId spEntityId) {
         if (spEntityId == null) {
            throw new NullPointerException("spEntityId is marked non-null but is null");
         }

         this.spEntityId = spEntityId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder idpEntityId(@NonNull Saml2IdpEntityId idpEntityId) {
         if (idpEntityId == null) {
            throw new NullPointerException("idpEntityId is marked non-null but is null");
         }

         this.idpEntityId = idpEntityId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder expiryInstant(@NonNull Instant expiryInstant) {
         if (expiryInstant == null) {
            throw new NullPointerException("expiryInstant is marked non-null but is null");
         }

         this.expiryInstant = expiryInstant;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder usernameToPropagate(@NonNull UsernameToPropagate usernameToPropagate) {
         if (usernameToPropagate == null) {
            throw new NullPointerException("usernameToPropagate is marked non-null but is null");
         }

         this.usernameToPropagate = usernameToPropagate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder authenticationInstant(@NonNull Instant authenticationInstant) {
         if (authenticationInstant == null) {
            throw new NullPointerException("authenticationInstant is marked non-null but is null");
         }

         this.authenticationInstant = authenticationInstant;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder assertionAttributes(@NonNull List<FlowSaml2Attribute> assertionAttributes) {
         if (assertionAttributes == null) {
            throw new NullPointerException("assertionAttributes is marked non-null but is null");
         }

         this.assertionAttributes = assertionAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder authenticationContext(
         @Nullable Saml2AuthenticationContextClassRef authenticationContext
      ) {
         this.authenticationContext = authenticationContext;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder relayState(@Nullable Saml2RelayState relayState) {
         this.relayState = relayState;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes.Saml2PropagationAttributesBuilder spInitiatedRequestId(@Nullable Saml2RequestId spInitiatedRequestId) {
         this.spInitiatedRequestId = spInitiatedRequestId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Saml2PropagationAttributes build() {
         return new Saml2PropagationAttributes(
            this.ssoContinuationId,
            this.spEntityId,
            this.idpEntityId,
            this.expiryInstant,
            this.usernameToPropagate,
            this.authenticationInstant,
            this.assertionAttributes,
            this.authenticationContext,
            this.relayState,
            this.spInitiatedRequestId
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Saml2PropagationAttributes.Saml2PropagationAttributesBuilder(ssoContinuationId="
            + this.ssoContinuationId
            + ", spEntityId="
            + this.spEntityId
            + ", idpEntityId="
            + this.idpEntityId
            + ", expiryInstant="
            + this.expiryInstant
            + ", usernameToPropagate="
            + this.usernameToPropagate
            + ", authenticationInstant="
            + this.authenticationInstant
            + ", assertionAttributes="
            + this.assertionAttributes
            + ", authenticationContext="
            + this.authenticationContext
            + ", relayState="
            + this.relayState
            + ", spInitiatedRequestId="
            + this.spInitiatedRequestId
            + ")";
      }
   }
}
