package com.airlock.iam.flow.shared.domain.service.identification;

import com.airlock.iam.authentication.api.domain.service.username.IdentificationResult;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.model.locking.LockReasons;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.common.domain.model.templock.TemporaryLocking;
import com.airlock.iam.common.domain.model.user.Lockable;
import com.airlock.iam.common.domain.model.user.UserLockState;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public final class UserValidityServiceImpl implements UserValidityService {
   private static final ELogger LOG = ELoggerFactory.getLogger(UserValidityServiceImpl.class);
   private final Provider<UserPrincipal> userPrincipal;
   private final Provider<Optional<TemporaryLocking>> temporaryLocking;

   @Inject
   public UserValidityServiceImpl(Provider<UserPrincipal> userPrincipal, Provider<Optional<TemporaryLocking>> temporaryLocking) {
      this.userPrincipal = userPrincipal;
      this.temporaryLocking = temporaryLocking;
   }

   @Override
   public IdentificationResult checkValidity(UserIdentity userIdentity) {
      UserPrincipal principal = (UserPrincipal)this.userPrincipal.get();
      if (!principal.exists()) {
         LOG.info(String.format("User %s not identified.", LogUtil.logSafeQuote(userIdentity.id())));
         return IdentificationResult.userNotFound(userIdentity);
      }

      if (!principal.getValidity().orElseThrow().isValid()) {
         LOG.info(String.format("User %s was identified, but is invalid.", LogUtil.logSafeQuote(userIdentity.id())));
         return IdentificationResult.userInvalid(userIdentity);
      }

      if (principal.asLockable().orElseThrow().isLocked()) {
         LOG.info(String.format("User %s was identified, but is locked.", LogUtil.logSafeQuote(userIdentity.id())));
         return IdentificationResult.userLocked(
            userIdentity, principal.asLockable().map(Lockable::lockState).flatMap(UserLockState::getReason).orElse(LockReasons.toLockReason("unknown"))
         );
      }

      if (((Optional)this.temporaryLocking.get()).isPresent()) {
         Optional<TemporaryLocking.TemporaryLock> temporaryLock = ((TemporaryLocking)((Optional)this.temporaryLocking.get()).get())
            .checkTemporaryLock(principal.user());
         if (temporaryLock.isPresent()) {
            LOG.info(String.format("User %s was identified, but is temporarily locked.", LogUtil.logSafeQuote(userIdentity.id())));
            return IdentificationResult.userTempLocked(userIdentity);
         }
      }

      return IdentificationResult.identified(userIdentity);
   }
}
