package com.airlock.iam.admin.infrastructure.factory.prot;

import com.airlock.iam.admin.application.configuration.users.UserGroupConfiguration;
import com.airlock.iam.admin.application.configuration.users.UsersConfiguration;
import com.airlock.iam.admin.domain.model.user.UserSpecificSettings;
import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.common.api.domain.model.authentication.Authentee;
import com.airlock.iam.common.api.domain.model.role.Role;
import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.application.exception.UsernameTransformationException;
import com.airlock.iam.common.infrastructure.targetuser.UriUserIdTransformer;
import com.airlock.iam.common.infrastructure.util.UriSegmentExtractor;
import com.google.inject.Exposed;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.servlet.RequestScoped;
import jakarta.inject.Named;
import jakarta.inject.Provider;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import org.glassfish.jersey.server.ExtendedUriInfo;

public class UserSpecificSettingsModule extends PrivateModule {
   private static final String USER_ID_SEGMENT_NAME = "userId";

   @Provides
   @RequestScoped
   Optional<PersistentUser> provideTargetUser(
      ExtendedUriInfo extendedUriInfo, UriUserIdTransformer transformer, @Named("MANAGED_USERS_STORE") UserStore userStore
   ) throws UsernameTransformationException {
      Optional<String> uriUserId = UriSegmentExtractor.extractSegmentWithId("userId", extendedUriInfo);
      Optional<String> userId = uriUserId.map(transformer::transform);
      return userId.isEmpty() ? Optional.empty() : userStore.findByIdentity(userId.get());
   }

   @RequestScoped
   @Provides
   @Exposed
   UserSpecificSettings provideUserSpecificSettings(
      Optional<UsersConfiguration> usersConfiguration, Provider<Optional<PersistentUser>> userProvider, @Named("authentee") Provider<Optional<Authentee>> admin
   ) {
      UsersConfiguration config = (UsersConfiguration)ServiceConfigurationException.checkPresent(usersConfiguration, "Users Configuration required");
      Optional<UserGroupConfiguration> groupConfig = ((Optional)userProvider.get()).flatMap(config::getGroupConfigurationForUser);
      return groupConfig.map(this::settingsFromUserGroup)
         .orElseGet(() -> this.genericUserSettings(config, ((Optional)admin.get()).<Set<Role>>map(Authentee::roles).orElse(Collections.emptySet())))
         .adminCannotDeleteSameNameUser(usersConfiguration.map(UsersConfiguration::isAdminCannotDeleteUserWithSameName).orElse(false))
         .build();
   }

   private UserSpecificSettings.UserSpecificSettingsBuilder settingsFromUserGroup(UserGroupConfiguration groupConfig) {
      return UserSpecificSettings.builder()
         .editable(groupConfig.isEditable())
         .tokenControllers(groupConfig.getAuthenticationTokens())
         .availableUserRoles(groupConfig.getAvailableUserRoles())
         .rowsOnUserDetailPage(groupConfig.getRowsOnUserDetailPage())
         .lockingSettings(groupConfig.getLockingSettings())
         .showCredentialMigrationSection(groupConfig.isShowCredentialMigrationSection())
         .showUserValidSection(groupConfig.isShowUserValidSection());
   }

   private UserSpecificSettings.UserSpecificSettingsBuilder genericUserSettings(UsersConfiguration config, Set<Role> adminRoles) {
      return UserSpecificSettings.builder()
         .editable(config.isEditable())
         .tokenControllers(config.getAuthenticationTokens())
         .availableUserRoles(config.getAvailableUserRoles(adminRoles))
         .rowsOnUserDetailPage(config.getRowsOnUserDetailPage())
         .lockingSettings(config.getLockingSettings())
         .showCredentialMigrationSection(config.isShowMigrationSection())
         .showUserValidSection(config.isShowUserValidSection());
   }

   protected void configure() {
   }
}
