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

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.domain.model.state.LockResult;
import com.airlock.iam.common.domain.service.SingleRequestPerUserService;
import com.airlock.iam.common.domain.service.exception.ExclusiveOperationDeniedException;
import com.airlock.iam.common.domain.service.state.StateRepository;
import com.airlock.iam.common.domain.service.state.StateRepositoryKey;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.shared.domain.model.session.SingleUserLock;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public final class SingleRequestPerUserServiceImpl implements SingleRequestPerUserService {
   private static final ELogger LOG = ELoggerFactory.getLogger(SingleRequestPerUserServiceImpl.class);
   private final Provider<StateRepository> stateRepository;
   private Optional<UserId> requestUserId = Optional.empty();
   private final Provider<SingleUserLock> singleUserLock;

   @Inject
   public SingleRequestPerUserServiceImpl(Provider<StateRepository> stateRepository, Provider<SingleUserLock> singleUserLock) {
      this.stateRepository = stateRepository;
      this.singleUserLock = singleUserLock;
   }

   @Override
   public synchronized void startOperatingExclusively(UserId userId) throws ExclusiveOperationDeniedException {
      if (this.requestUserId.isPresent()) {
         if (!this.requestUserId.get().equals(userId)) {
            throw new IllegalStateException("Tried to operate on more than one user within the same request.");
         }
      } else {
         LockResult lockResult = ((StateRepository)this.stateRepository.get()).lock(this.keyForUserId(userId));
         if (lockResult.lockAcquired()) {
            this.requestUserId = Optional.of(userId);
            ((SingleUserLock)this.singleUserLock.get()).setUserId(userId);
            LOG.debug(String.format("Acquired right to operate on '%s' exclusively.", LogUtil.logSafeString(userId)));
         } else {
            String message = String.format("Could not acquire right to operate on '%s' exclusively.", LogUtil.logSafeString(userId));
            LOG.info(message);
            throw new ExclusiveOperationDeniedException(message);
         }
      }
   }

   private StateRepositoryKey keyForUserId(UserId userId) {
      return new StateRepositoryKey(userId.getValue(), StateRepositoryKey.StandardStateEntryType.USER);
   }
}
