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

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.user.ProvidedUsername;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.api.domain.model.user.transformation.UsernameTransformation;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.common.domain.model.transform.TransformWithLogContextUpdate;
import com.airlock.iam.common.domain.service.SingleRequestPerUserService;
import com.airlock.iam.common.domain.service.exception.ExclusiveOperationDeniedException;
import com.airlock.iam.common.domain.service.principal.UserPrincipalService;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

public final class UserIdentificationServiceImpl implements UserIdentificationService {
   private static final ELogger LOG = ELoggerFactory.getLogger(UserIdentificationServiceImpl.class);
   private final Provider<UsernameTransformation> usernameTransformation;
   private final Provider<SingleRequestPerUserService> singleRequestPerUserService;
   private final Provider<UserPrincipal> principal;
   private final Provider<UserPrincipalService> userPrincipalService;

   @Inject
   public UserIdentificationServiceImpl(
      Provider<UsernameTransformation> usernameTransformation,
      Provider<SingleRequestPerUserService> singleRequestPerUserService,
      Provider<UserPrincipal> principal,
      Provider<UserPrincipalService> userPrincipalService
   ) {
      this.usernameTransformation = usernameTransformation;
      this.singleRequestPerUserService = singleRequestPerUserService;
      this.principal = principal;
      this.userPrincipalService = userPrincipalService;
   }

   @Override
   public UserIdentity identifyUser(ProvidedUsername providedUsername, UsernameTransformation preTransformation) throws ExclusiveOperationDeniedException {
      UserId transformedId = this.transformUsername(providedUsername, preTransformation);
      UserIdentity userIdentity = new UserIdentity(transformedId, providedUsername);
      if (((UserPrincipal)this.principal.get()).exists() && !((UserPrincipal)this.principal.get()).hasUserId(userIdentity)) {
         return userIdentity;
      }

      UserPrincipal loadedUserPrincipal;
      if (!((UserPrincipal)this.principal.get()).exists()) {
         loadedUserPrincipal = ((UserPrincipalService)this.userPrincipalService.get()).initializePrincipal(userIdentity);
         LOG.debug("Loaded tentative user " + LogUtil.logSafeString(loadedUserPrincipal.getIdentity().map(UserIdentity::toLogString).orElseThrow()));
      } else {
         loadedUserPrincipal = (UserPrincipal)this.principal.get();
      }

      UserId finalUserId = loadedUserPrincipal.getIdentity().map(UserIdentity::id).orElse(transformedId);
      UserIdentity finalUserIdentity = new UserIdentity(finalUserId, providedUsername);
      ((SingleRequestPerUserService)this.singleRequestPerUserService.get()).startOperatingExclusively(finalUserIdentity.id());
      return finalUserIdentity;
   }

   private UserId transformUsername(ProvidedUsername providedUsername, UsernameTransformation preTransformation) {
      return TransformWithLogContextUpdate.transformAndUpdateLogContext(
         preTransformation.chainWith((UsernameTransformation)this.usernameTransformation.get()), providedUsername
      );
   }
}
