package com.airlock.iam.admin.application.configuration.users;

import com.airlock.iam.common.api.domain.model.role.Role;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Plugin(description = "Used to overwrite certain behaviour for administrators with specified roles.")
public class AdminRoleSpecificSetting {
   @StringListProperty(
      description = "The roles to match the administrator's roles for selecting the settings defined in this plugin. If multiple required roles are configured, the configured \"Role Specific Settings Selection\" determines whether or not the roles are matching.",
      optional = false,
      displayName = "Required Admin Roles",
      exampleElements = {"useradmin", "sysadmin", "useradmin,tokenadmin", "useradmin,sysadmin"}
   )
   private List<String> requiredRoles;
   static final String REQUIRED_ROLES_PROPERTY = "requiredRoles";
   static final String REQUIRED_ROLES_DISPLAY_NAME = "Required Admin Roles";
   @PluginProperty(
      description = "<p>The User Data Source to be used for administrators with matching \"Required Admin Roles\" configured above.</p><p>If not configured, the User Data Source from the Users Configuration is used.</p>",
      optional = true,
      displayName = "User Data Source"
   )
   private UserRepositoryConfiguration userDataSource;
   @StringListProperty(
      description = "<p>Set of roles assignable to users for administrators with matching \"Required Admin Roles\" configured above.</p>\n<p>Translations for the roles displayed in the Adminapp user management UI can be defined using the\nAdminapp translation keys <tt>roles.user.labels.[rolename]</tt>.</p>\n<p>If no roles are configured, the default user roles are used.</p>",
      exampleElements = {"employee", "customer"},
      optional = true
   )
   private List<String> availableUserRoles;

   AdminRoleSpecificSetting(UserRepositoryConfiguration userRepositoryConfiguration, List<String> availableUserRoles, List<String> requiredRoles) {
      this.userDataSource = userRepositoryConfiguration;
      this.availableUserRoles = availableUserRoles;
      this.requiredRoles = requiredRoles;
   }

   public Optional<UserRepositoryConfiguration> getUserRepositoryConfiguration() {
      return Optional.ofNullable(this.userDataSource);
   }

   public List<String> getAvailableUserRoles() {
      return this.availableUserRoles;
   }

   public Set<Role> getRequiredAdminRoles() {
      return this.requiredRoles.stream().<Role>map(Role::parseRole).collect(Collectors.toSet());
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public AdminRoleSpecificSetting() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class AdminRoleSpecificSettingBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserRepositoryConfiguration userRepositoryConfiguration;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> availableUserRoles;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> requiredRoles;

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

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder userRepositoryConfiguration(UserRepositoryConfiguration userRepositoryConfiguration) {
         this.userRepositoryConfiguration = userRepositoryConfiguration;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder availableUserRole(String availableUserRole) {
         if (this.availableUserRoles == null) {
            this.availableUserRoles = new ArrayList<>();
         }

         this.availableUserRoles.add(availableUserRole);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder availableUserRoles(Collection<? extends String> availableUserRoles) {
         if (availableUserRoles == null) {
            throw new NullPointerException("availableUserRoles cannot be null");
         }

         if (this.availableUserRoles == null) {
            this.availableUserRoles = new ArrayList<>();
         }

         this.availableUserRoles.addAll(availableUserRoles);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder clearAvailableUserRoles() {
         if (this.availableUserRoles != null) {
            this.availableUserRoles.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder requiredRole(String requiredRole) {
         if (this.requiredRoles == null) {
            this.requiredRoles = new ArrayList<>();
         }

         this.requiredRoles.add(requiredRole);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder requiredRoles(Collection<? extends String> requiredRoles) {
         if (requiredRoles == null) {
            throw new NullPointerException("requiredRoles cannot be null");
         }

         if (this.requiredRoles == null) {
            this.requiredRoles = new ArrayList<>();
         }

         this.requiredRoles.addAll(requiredRoles);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder clearRequiredRoles() {
         if (this.requiredRoles != null) {
            this.requiredRoles.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdminRoleSpecificSetting build() {
         List availableUserRoles = switch (this.availableUserRoles == null ? 0 : this.availableUserRoles.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.availableUserRoles.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.availableUserRoles));
         };

         return new AdminRoleSpecificSetting(
            this.userRepositoryConfiguration, availableUserRoles, switch (this.requiredRoles == null ? 0 : this.requiredRoles.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.requiredRoles.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.requiredRoles));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "AdminRoleSpecificSetting.AdminRoleSpecificSettingBuilder(userRepositoryConfiguration="
            + this.userRepositoryConfiguration
            + ", availableUserRoles="
            + this.availableUserRoles
            + ", requiredRoles="
            + this.requiredRoles
            + ")";
      }
   }
}
