package com.airlock.iam.publicselfservice.domain.service;

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
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.principal.UserPrincipal;
import com.airlock.iam.common.domain.service.principal.UserPrincipalService;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.publicselfservice.api.domain.model.PublicSelfServiceRestriction;
import com.airlock.iam.publicselfservice.api.domain.model.PublicSelfServiceUser;
import com.airlock.iam.publicselfservice.domain.model.user.PublicSelfServiceUserImpl;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;

public final class PublicSelfServiceUserFactoryImpl implements PublicSelfServiceUserFactory {
   private final Provider<UserPrincipal> userPrincipal;
   private final Provider<UserPrincipalService> userPrincipalService;
   private final List<PublicSelfServiceRestriction> restrictions;
   private final HelpdeskLogger helpdeskLogger;

   @Inject
   public PublicSelfServiceUserFactoryImpl(
      Provider<UserPrincipal> userPrincipal,
      Provider<UserPrincipalService> userPrincipalService,
      List<PublicSelfServiceRestriction> restrictions,
      HelpdeskLogger helpdeskLogger
   ) {
      this.userPrincipal = userPrincipal;
      this.userPrincipalService = userPrincipalService;
      this.restrictions = restrictions;
      this.helpdeskLogger = helpdeskLogger;
   }

   @Override
   public PublicSelfServiceUser createPublicSelfServiceUser(UserIdentity identity) {
      if (((UserPrincipal)this.userPrincipal.get()).exists() && !((UserPrincipal)this.userPrincipal.get()).hasUserId(identity)) {
         throw new ViolationException(
            "Conflicting IDs when creating Public Self-Service User: requested: "
               + identity.id()
               + " principal: "
               + ((UserPrincipal)this.userPrincipal.get()).getId()
         );
      }

      Optional<PublicSelfServiceRestriction.RestrictionViolation> violation = this.determineIfAnyRestrictionViolated(identity);
      return PublicSelfServiceUserImpl.builder()
         .identity(identity)
         .restrictionViolation(violation)
         .userPrincipal((UserPrincipal)this.userPrincipal.get())
         .userPrincipalService((UserPrincipalService)this.userPrincipalService.get())
         .build();
   }

   private Optional<PublicSelfServiceRestriction.RestrictionViolation> determineIfAnyRestrictionViolated(UserIdentity identity) {
      for (PublicSelfServiceRestriction restriction : this.restrictions) {
         Optional<PublicSelfServiceRestriction.RestrictionViolation> violation = restriction.getViolation();
         if (violation.isPresent()) {
            this.logViolation(identity, violation.get());
            return violation;
         }
      }

      return Optional.empty();
   }

   private void logViolation(UserIdentity identity, PublicSelfServiceRestriction.RestrictionViolation violation) {
      this.helpdeskLogger
         .log(LogUtil.logSafeString(identity.id()), "Not allowed to perform public self-service because of violated restriction: " + violation.getCode().name());
   }
}
