package com.airlock.iam.oauth2.domain.model.accountregistration;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.api.domain.model.store.user.exception.UserEntityNotUniqueException;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.domain.model.user.Lockable;
import com.airlock.iam.common.domain.model.user.LockableImpl;
import com.airlock.iam.common.domain.service.adapt.PersistentUserAdapter;
import com.airlock.iam.common.infrastructure.adapt.PersistentUserAdapterImpl;
import com.airlock.iam.core.misc.authen.PredefinedLockReason;
import com.airlock.iam.core.misc.impl.persistency.SimplePersistentUserWithPassword;
import com.airlock.iam.login.api.domain.model.oauth2.OAuth2Credential;
import com.airlock.iam.login.api.domain.model.oauth2.accountregistration.AccountRegistrationUsername;
import com.airlock.iam.login.api.domain.model.oauth2.accountregistration.AccountRegistrationUsernameProvider;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.Optional;

public final class AccountRegistrationUserFactory {
   private final UserStore userStore;
   private final AccountRegistrationUsernameProvider usernameProvider;
   private final List<AccountRegistrationContextDataExtractor> contextDataExtractors;
   private final List<String> staticRoles;
   private final boolean lockUsers;
   private final PersistentUserAdapter<Lockable> lockableAdapter = PersistentUserAdapterImpl.create(LockableImpl.class);

   public SimplePersistentUserWithPassword createUser(OAuth2Credential credential) throws UserEntityNotUniqueException, AccountRegistrationContextDataExtractor.MandatoryContextDataMissingException {
      AccountRegistrationUsername username = this.usernameProvider.provide(credential, this.userStore);
      Optional<PersistentUser> existingUser = this.userStore.findByIdentity(username.getUsername());
      if (existingUser.isPresent()) {
         throw new UserEntityNotUniqueException(UserId.userId(existingUser.get().getName()));
      }

      SimplePersistentUserWithPassword user = new SimplePersistentUserWithPassword(username.getUsername(), this.staticRoles.toArray(String[]::new), null);

      for (AccountRegistrationContextDataExtractor extractor : this.contextDataExtractors) {
         extractor.contextData(credential)
            .ifPresent(contextData -> user.putContextDataValue(contextData.getContextDataName(), contextData.getContextDataValue()));
      }

      if (this.lockUsers) {
         this.lockableAdapter.to(user).lock(PredefinedLockReason.AWAITING_ADMIN_APPROVAL);
      }

      return user;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder builder() {
      return new AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public AccountRegistrationUserFactory(
      UserStore userStore,
      AccountRegistrationUsernameProvider usernameProvider,
      List<AccountRegistrationContextDataExtractor> contextDataExtractors,
      List<String> staticRoles,
      boolean lockUsers
   ) {
      this.userStore = userStore;
      this.usernameProvider = usernameProvider;
      this.contextDataExtractors = contextDataExtractors;
      this.staticRoles = staticRoles;
      this.lockUsers = lockUsers;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class AccountRegistrationUserFactoryBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserStore userStore;
      @SuppressFBWarnings(justification = "generated code")
      private AccountRegistrationUsernameProvider usernameProvider;
      @SuppressFBWarnings(justification = "generated code")
      private List<AccountRegistrationContextDataExtractor> contextDataExtractors;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> staticRoles;
      @SuppressFBWarnings(justification = "generated code")
      private boolean lockUsers;

      @SuppressFBWarnings(justification = "generated code")
      AccountRegistrationUserFactoryBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder userStore(UserStore userStore) {
         this.userStore = userStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder usernameProvider(AccountRegistrationUsernameProvider usernameProvider) {
         this.usernameProvider = usernameProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder contextDataExtractors(
         List<AccountRegistrationContextDataExtractor> contextDataExtractors
      ) {
         this.contextDataExtractors = contextDataExtractors;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder staticRoles(List<String> staticRoles) {
         this.staticRoles = staticRoles;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder lockUsers(boolean lockUsers) {
         this.lockUsers = lockUsers;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountRegistrationUserFactory build() {
         return new AccountRegistrationUserFactory(this.userStore, this.usernameProvider, this.contextDataExtractors, this.staticRoles, this.lockUsers);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "AccountRegistrationUserFactory.AccountRegistrationUserFactoryBuilder(userStore="
            + this.userStore
            + ", usernameProvider="
            + this.usernameProvider
            + ", contextDataExtractors="
            + this.contextDataExtractors
            + ", staticRoles="
            + this.staticRoles
            + ", lockUsers="
            + this.lockUsers
            + ")";
      }
   }
}
