package com.airlock.iam.flow.shared.domain.model.valueprovider;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.common.api.domain.model.encoder.StringEncoder;
import com.airlock.iam.common.api.domain.model.role.Role;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.flow.shared.api.domain.model.valueprovider.ValueMapProvider;
import com.airlock.iam.flow.shared.application.configuration.valueprovider.RolesProviderConfig;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

@LogicForConfig(RolesProviderConfig.class)
public final class RolesProvider implements ValueMapProvider {
   private final List<RoleProvider> roleProviders;
   private final List<StringEncoder> encoders;
   private final RolesProviderConfig config;
   private final Provider<PrincipalInfo> user;

   @Inject
   public RolesProvider(List<RoleProvider> roleProviders, List<StringEncoder> encoders, RolesProviderConfig config, Provider<PrincipalInfo> user) {
      this.roleProviders = roleProviders;
      this.encoders = encoders;
      this.config = config;
      this.user = user;
   }

   @Override
   public Map<String, Object> provideValueMap() {
      Optional<UserIdentity> identity = ((PrincipalInfo)this.user.get()).getKnownIdentity();
      if (identity.isEmpty()) {
         return Collections.emptyMap();
      }

      List<Role> rolesList = this.roleProviders.stream().flatMap(provider -> provider.provideRoles(identity.get()).stream()).collect(Collectors.toList());
      if (rolesList.isEmpty()) {
         return Collections.emptyMap();
      }

      String roles = this.joinToString(rolesList, Role::formatRole);
      String roleNames = this.joinToString(rolesList, Role::getRoleName);
      List<String> formatedRoles = rolesList.stream().map(Role::formatRole).sorted().toList();
      List<String> namedRoles = rolesList.stream().map(Role::getRoleName).sorted().toList();
      return Map.of("roles", roles, "role-names", roleNames, "roles-list", formatedRoles, "role-names-list", namedRoles);
   }

   private String joinToString(List<Role> rolesList, Function<Role, String> toString) {
      return rolesList.stream().map(role -> this.encodeValue(toString.apply(role))).sorted().collect(Collectors.joining(this.config.getSeparator()));
   }

   private String encodeValue(String value) {
      String encoded = value;

      for (StringEncoder encoder : this.encoders) {
         encoded = encoder.encodeString(encoded);
      }

      return encoded;
   }
}
