package com.airlock.iam.admin.application.service.settings;

import com.airlock.iam.admin.application.service.user.OAuth2AdminService;
import com.airlock.iam.admin.domain.model.administrator.permissions.AdminappAction;
import com.airlock.iam.admin.domain.model.token.TokenController;
import com.airlock.iam.admin.domain.service.access.AdminappAccessController;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.core.misc.cronto.CrontoHandler;
import com.airlock.iam.core.misc.cronto.CrontoHandler.PlatformType;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.function.Function;

public final class TokenControllerSettingsBuilder {
   private final Provider<TokenController.TokenControllers> tokenControllers;
   private final Provider<AdminappAccessController> accessController;
   private final Provider<OAuth2AdminService> oAuth2AdminService;

   @Inject
   TokenControllerSettingsBuilder(
      Provider<TokenController.TokenControllers> tokenControllers,
      Provider<AdminappAccessController> accessController,
      Provider<OAuth2AdminService> oAuth2AdminService
   ) {
      this.tokenControllers = tokenControllers;
      this.accessController = accessController;
      this.oAuth2AdminService = oAuth2AdminService;
   }

   Map<String, Object> build(UserId userId) {
      Builder<String, Object> settings = ImmutableMap.builder();
      this.passwordTokenControllerSettings().ifPresent(settings::put);
      this.mtanTokenControllerSettings().ifPresent(settings::put);
      this.crontoTokenControllerSettings().ifPresent(settings::put);
      this.airlock2FATokenControllerSettings().ifPresent(settings::put);
      this.oathOtpTokenControllerSettings().ifPresent(settings::put);
      this.vascoOtpTokenControllerSettings().ifPresent(settings::put);
      this.oauth2TokenControllerSettings(userId).ifPresent(settings::put);
      return settings.build();
   }

   private Optional<Entry<String, Map<String, Object>>> passwordTokenControllerSettings() {
      return this.tokenControllerSettings(
         "PASSWORD",
         tokenController -> Map.ofEntries(
            Map.entry("showLetterAttributes", tokenController.showLetterAttributes()), Map.entry("hasPasswordRenderer", tokenController.hasPasswordRenderer())
         )
      );
   }

   private Optional<Entry<String, Map<String, Object>>> mtanTokenControllerSettings() {
      return this.tokenControllerSettings(
         "MTAN",
         tokenController -> {
            Builder<String, Object> map = ImmutableMap.builder();
            if (tokenController.getOtpMessageTemplate() != null) {
               map.put("otpMessageTemplate", tokenController.getOtpMessageTemplate());
            }

            map.put("showLetterAttributes", tokenController.showLetterAttributes())
               .put("maxNumberOfTokens", tokenController.maxNumberOfTokens())
               .put("supportsLabel", tokenController.supportsLabel())
               .put("supportsFlash", tokenController.getMtanSettings().getMtanHandler().supportsFlash())
               .put("isCredentialBased", tokenController.getMtanSettings().isCredentialBased());
            return map.build();
         }
      );
   }

   private Optional<Entry<String, Map<String, Object>>> crontoTokenControllerSettings() {
      return this.tokenControllerSettings(
         "CRONTO",
         tokenController -> {
            CrontoHandler handler = tokenController.getCrontoHandler();
            return Map.ofEntries(
               Map.entry("platforms", this.getCrontoPlatformIdentifiers(List.of(PlatformType.values()))),
               Map.entry("platformsBlacklist", this.getCrontoPlatformIdentifiers(handler.getBlackListedPlatformTypes())),
               Map.entry("availablePrintingOptions", handler.getAvailablePrintingOptions()),
               Map.entry("printingOptionsResourceKeyPrefix", handler.getOptionsResourceKeyPrefix()),
               Map.entry("pushNotificationsEnabled", handler.isPushNotificationEnabled())
            );
         }
      );
   }

   private Optional<Entry<String, Map<String, Object>>> airlock2FATokenControllerSettings() {
      return this.tokenControllerSettings(
         "AIRLOCK_2FA",
         tokenController -> Map.ofEntries(
            Map.entry("createActivationLetterEnabled", tokenController.getCreateActivationLetters().isPresent()),
            Map.entry("orderActivationLetterEnabled", tokenController.getOrderActivationLetters().isPresent()),
            Map.entry("createShipmentLetterEnabled", tokenController.getCreateShipmentLetters().isPresent()),
            Map.entry("shareHardwareTokensAmongUsers", tokenController.isShareHardwareTokensAmongUsers())
         )
      );
   }

   private Optional<Entry<String, Map<String, Object>>> oathOtpTokenControllerSettings() {
      return this.tokenControllerSettings(
         "OATH_OTP", tokenController -> Map.of("tokenType", tokenController.getOathOtpSettings().isTimeBased() ? "TOTP" : "HOTP")
      );
   }

   private Optional<Entry<String, Map<String, Object>>> vascoOtpTokenControllerSettings() {
      return this.tokenControllerSettings(
         "VASCO_OTP",
         tokenController -> {
            Builder<String, Object> map = ImmutableMap.builder()
               .put("additionalInformation", tokenController.getAvailableAdditionalInformation())
               .put("allowEmptyAdditionalInformation", tokenController.isAllowEmptyAdditionalInformation());
            if (tokenController.getAdditionalInfoResourceKeyPrefix() != null) {
               map.put("additionalInformationResourceKeyPrefix", tokenController.getAdditionalInfoResourceKeyPrefix());
            }

            map.put("allowDirectPrint", tokenController.getVascoLetterGenerator() != null)
               .put("confirmAssignment", tokenController.isShowWarningWithinTokenAssignmentDialog())
               .put("enableComment", tokenController.isEnableComment())
               .put("allowEmptyComment", tokenController.isAllowEmptyComment())
               .put("onlyActiveDevicesAssignable", tokenController.isOnlyActiveTokensAssignable())
               .put("assignDevicesToMultipleUsers", tokenController.isAssignTokenToMultipleUsers());
            return map.build();
         }
      );
   }

   private Optional<Entry<String, Map<String, Object>>> oauth2TokenControllerSettings(UserId userId) {
      return this.tokenControllerSettings(
         "OAUTH2",
         controller -> Map.of(
            "showConsentManagement",
            controller.consentRepository().isPresent()
               && ((AdminappAccessController)this.accessController.get()).isActionAllowed(AdminappAction.MANAGE_OAUTH_CONSENTS),
            "showSessionTokenManagement",
            this.hasOAuth2SessionTokens(userId)
         )
      );
   }

   private boolean hasOAuth2SessionTokens(UserId userId) {
      return ((OAuth2AdminService)this.oAuth2AdminService.get()).getNumberOfOAuth2Tokens(userId) > 0;
   }

   private <T extends TokenController> Optional<Entry<String, Map<String, Object>>> tokenControllerSettings(
      String tokenControllerId, Function<T, Map<String, Object>> tokenControllerSettings
   ) {
      Optional<T> tokenController = ((TokenController.TokenControllers)this.tokenControllers.get()).ofId(tokenControllerId);
      return tokenController.map(tokenControllerSettings).map(settings -> Map.entry(tokenControllerId, (Map<String, Object>)settings));
   }

   private List<String> getCrontoPlatformIdentifiers(Collection<PlatformType> platforms) {
      return platforms.stream().<String>map(PlatformType::getIdentifier).toList();
   }
}
