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

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.core.domain.model.token.TokenUsage;
import com.airlock.iam.factor.domain.model.fido.registration.FidoAttestationCertificate;
import com.airlock.iam.fido.api.domain.model.FidoCredential;
import com.airlock.iam.fido.api.domain.model.FidoCredentialId;
import com.airlock.iam.fido.api.domain.model.FidoPublicKeyCredentialId;
import com.airlock.iam.fido.api.domain.model.FidoRelyingPartyId;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nonnull;
import java.time.Instant;
import java.util.Date;
import java.util.Optional;
import lombok.NonNull;

public final class FidoUserCredential implements FidoCredential {
   @NonNull
   private final FidoCredentialId id;
   @NonNull
   private final UserId userId;
   @NonNull
   private final FidoCredentialName name;
   @NonNull
   private final FidoRelyingPartyId relyingPartyId;
   @NonNull
   private final FidoPublicKeyCredentialId publicKeyCredentialId;
   @NonNull
   private final FidoPasswordlessCapability passwordless;
   @NonNull
   private final Optional<FidoAttestationCertificate> attestationCertificate;
   @NonNull
   private final Optional<FidoAuthenticatorInfo> authenticatorInfo;
   @NonNull
   private final Instant registeredAt;
   @NonNull
   private final TokenUsage usage;
   @NonNull
   private FidoCredentialDisplayName displayName;
   private boolean locked;

   @Nonnull
   public FidoCredentialId getId() {
      return this.id;
   }

   public Optional<Instant> firstUsedAt() {
      return Optional.ofNullable(this.usage.getFirstUsageDate()).map(Date::toInstant);
   }

   public Optional<Instant> lastUsedAt() {
      return Optional.ofNullable(this.usage.getMostRecentUsageDate()).map(Date::toInstant);
   }

   public Integer totalUsages() {
      return this.usage.getTotalUsages();
   }

   public boolean lock() {
      if (this.locked) {
         return false;
      }

      this.locked = true;
      return true;
   }

   public boolean unlock() {
      if (!this.locked) {
         return false;
      }

      this.locked = false;
      return true;
   }

   public void setDisplayName(String newDisplayName) {
      this.displayName = FidoCredentialDisplayName.credentialDisplayName(newDisplayName);
   }

   @SuppressFBWarnings(justification = "generated code")
   FidoUserCredential(
      @NonNull FidoCredentialId id,
      @NonNull UserId userId,
      @NonNull FidoCredentialName name,
      @NonNull FidoRelyingPartyId relyingPartyId,
      @NonNull FidoPublicKeyCredentialId publicKeyCredentialId,
      @NonNull FidoPasswordlessCapability passwordless,
      @NonNull Optional<FidoAttestationCertificate> attestationCertificate,
      @NonNull Optional<FidoAuthenticatorInfo> authenticatorInfo,
      @NonNull Instant registeredAt,
      @NonNull TokenUsage usage,
      @NonNull FidoCredentialDisplayName displayName,
      boolean locked
   ) {
      if (id == null) {
         throw new NullPointerException("id is marked non-null but is null");
      }

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

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

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

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

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

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

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

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

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

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

      this.id = id;
      this.userId = userId;
      this.name = name;
      this.relyingPartyId = relyingPartyId;
      this.publicKeyCredentialId = publicKeyCredentialId;
      this.passwordless = passwordless;
      this.attestationCertificate = attestationCertificate;
      this.authenticatorInfo = authenticatorInfo;
      this.registeredAt = registeredAt;
      this.usage = usage;
      this.displayName = displayName;
      this.locked = locked;
   }

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

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

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

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public String toString() {
      return "FidoUserCredential(userId="
         + this.getUserId()
         + ", name="
         + this.getName()
         + ", relyingPartyId="
         + this.getRelyingPartyId()
         + ", registeredAt="
         + this.getRegisteredAt()
         + ", usage="
         + this.usage
         + ", displayName="
         + this.getDisplayName()
         + ", locked="
         + this.isLocked()
         + ")";
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public UserId getUserId() {
      return this.userId;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public FidoCredentialName getName() {
      return this.name;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public FidoRelyingPartyId getRelyingPartyId() {
      return this.relyingPartyId;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public FidoPublicKeyCredentialId getPublicKeyCredentialId() {
      return this.publicKeyCredentialId;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public FidoPasswordlessCapability getPasswordless() {
      return this.passwordless;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Optional<FidoAttestationCertificate> getAttestationCertificate() {
      return this.attestationCertificate;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public Optional<FidoAuthenticatorInfo> getAuthenticatorInfo() {
      return this.authenticatorInfo;
   }

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

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public FidoCredentialDisplayName getDisplayName() {
      return this.displayName;
   }

   @SuppressFBWarnings(justification = "generated code")
   public boolean isLocked() {
      return this.locked;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class FidoUserCredentialBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FidoCredentialId id;
      @SuppressFBWarnings(justification = "generated code")
      private UserId userId;
      @SuppressFBWarnings(justification = "generated code")
      private FidoCredentialName name;
      @SuppressFBWarnings(justification = "generated code")
      private FidoRelyingPartyId relyingPartyId;
      @SuppressFBWarnings(justification = "generated code")
      private FidoPublicKeyCredentialId publicKeyCredentialId;
      @SuppressFBWarnings(justification = "generated code")
      private FidoPasswordlessCapability passwordless;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<FidoAttestationCertificate> attestationCertificate;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<FidoAuthenticatorInfo> authenticatorInfo;
      @SuppressFBWarnings(justification = "generated code")
      private Instant registeredAt;
      @SuppressFBWarnings(justification = "generated code")
      private TokenUsage usage;
      @SuppressFBWarnings(justification = "generated code")
      private FidoCredentialDisplayName displayName;
      @SuppressFBWarnings(justification = "generated code")
      private boolean locked;

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

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder id(@NonNull FidoCredentialId id) {
         if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
         }

         this.id = id;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder userId(@NonNull UserId userId) {
         if (userId == null) {
            throw new NullPointerException("userId is marked non-null but is null");
         }

         this.userId = userId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder name(@NonNull FidoCredentialName name) {
         if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
         }

         this.name = name;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder relyingPartyId(@NonNull FidoRelyingPartyId relyingPartyId) {
         if (relyingPartyId == null) {
            throw new NullPointerException("relyingPartyId is marked non-null but is null");
         }

         this.relyingPartyId = relyingPartyId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder publicKeyCredentialId(@NonNull FidoPublicKeyCredentialId publicKeyCredentialId) {
         if (publicKeyCredentialId == null) {
            throw new NullPointerException("publicKeyCredentialId is marked non-null but is null");
         }

         this.publicKeyCredentialId = publicKeyCredentialId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder passwordless(@NonNull FidoPasswordlessCapability passwordless) {
         if (passwordless == null) {
            throw new NullPointerException("passwordless is marked non-null but is null");
         }

         this.passwordless = passwordless;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder attestationCertificate(@NonNull Optional<FidoAttestationCertificate> attestationCertificate) {
         if (attestationCertificate == null) {
            throw new NullPointerException("attestationCertificate is marked non-null but is null");
         }

         this.attestationCertificate = attestationCertificate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder authenticatorInfo(@NonNull Optional<FidoAuthenticatorInfo> authenticatorInfo) {
         if (authenticatorInfo == null) {
            throw new NullPointerException("authenticatorInfo is marked non-null but is null");
         }

         this.authenticatorInfo = authenticatorInfo;
         return this;
      }

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

         this.registeredAt = registeredAt;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder usage(@NonNull TokenUsage usage) {
         if (usage == null) {
            throw new NullPointerException("usage is marked non-null but is null");
         }

         this.usage = usage;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder displayName(@NonNull FidoCredentialDisplayName displayName) {
         if (displayName == null) {
            throw new NullPointerException("displayName is marked non-null but is null");
         }

         this.displayName = displayName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential.FidoUserCredentialBuilder locked(boolean locked) {
         this.locked = locked;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoUserCredential build() {
         return new FidoUserCredential(
            this.id,
            this.userId,
            this.name,
            this.relyingPartyId,
            this.publicKeyCredentialId,
            this.passwordless,
            this.attestationCertificate,
            this.authenticatorInfo,
            this.registeredAt,
            this.usage,
            this.displayName,
            this.locked
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "FidoUserCredential.FidoUserCredentialBuilder(id="
            + this.id
            + ", userId="
            + this.userId
            + ", name="
            + this.name
            + ", relyingPartyId="
            + this.relyingPartyId
            + ", publicKeyCredentialId="
            + this.publicKeyCredentialId
            + ", passwordless="
            + this.passwordless
            + ", attestationCertificate="
            + this.attestationCertificate
            + ", authenticatorInfo="
            + this.authenticatorInfo
            + ", registeredAt="
            + this.registeredAt
            + ", usage="
            + this.usage
            + ", displayName="
            + this.displayName
            + ", locked="
            + this.locked
            + ")";
      }
   }
}
