package com.airlock.iam.publicselfservice.domain.model.user;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.common.api.domain.model.locking.LockReason;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.domain.model.locking.LockReasons;
import com.airlock.iam.common.domain.model.user.Lockable;
import com.airlock.iam.common.domain.model.user.UserLockState;
import com.airlock.iam.log.api.signing.SigningLogger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
import java.util.List;
import java.util.Optional;

public final class PublicSelfServiceLockable implements Lockable {
   private final boolean resetFailedLoginsForHtmlLoginapp;
   private final boolean countUnlockAttempts;
   private final List<AuthenticationMethod> authenticationMethods;
   private final PersistentUser user;
   private final SigningLogger signingLogger;
   private boolean stateUpdated;

   @Override
   public String id() {
      return this.user.getName();
   }

   @Override
   public void lock(LockReason reason) {
      this.lock(reason.getResourceKey());
   }

   @Override
   public void lock(String reason) {
      this.user.setLocked();
      this.user.setLockReason(reason);
      this.user.setLockDate(new Date());
      this.stateUpdated = true;
   }

   @Override
   public void unlock() {
      this.user.setLocked(false);
      this.user.setLockDate(null);
      this.user.setLockReason(null);
      this.increaseUnlockAttempts();
      this.resetFailedLogins();
      this.user.getAllFailedLogins().resetFailedLoginCountersForMethods(this.authenticationMethods);
      this.stateUpdated = true;
   }

   private void resetFailedLogins() {
      if (this.resetFailedLoginsForHtmlLoginapp) {
         this.user.resetFailedLogins();
      }
   }

   private void increaseUnlockAttempts() {
      if (this.countUnlockAttempts) {
         this.user.increaseUnlockAttempts();
         this.signingLogger
            .log("Increased unlock counter for user " + LogUtil.quote(this.user.getName()) + " to " + LogUtil.quote(this.user.getUnlockAttempts()) + ".");
         this.user.setLatestUnlockAttempt(new Date());
      }
   }

   @Override
   public UserLockState lockState() {
      return UserLockState.builder()
         .userId(UserId.userId(this.user.getName()))
         .reason(Optional.ofNullable(this.user.getLockReason()).map(LockReasons::toLockReason))
         .locked(this.user.isLocked())
         .lockDate(this.user.getLockDate())
         .stateUpdated(this.stateUpdated)
         .build();
   }

   @SuppressFBWarnings(justification = "generated code")
   public PublicSelfServiceLockable(
      boolean resetFailedLoginsForHtmlLoginapp,
      boolean countUnlockAttempts,
      List<AuthenticationMethod> authenticationMethods,
      PersistentUser user,
      SigningLogger signingLogger
   ) {
      this.resetFailedLoginsForHtmlLoginapp = resetFailedLoginsForHtmlLoginapp;
      this.countUnlockAttempts = countUnlockAttempts;
      this.authenticationMethods = authenticationMethods;
      this.user = user;
      this.signingLogger = signingLogger;
   }
}
