package com.airlock.iam.servicecontainer.app.application.configuration.radius;

import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.elog.api.LogTypes;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.servicecontainer.app.application.service.radius.service.RadiusRolesConfiguration;
import com.airlock.iam.servicecontainer.app.application.service.radius.wrapper.context.IamRadiusServerContext;
import com.airlock.iam.servicecontainer.app.application.service.radius.wrapper.lib.IamRadiusServerAttribute;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.commons.lang3.StringUtils;

@Plugin(
   description = "Configures the RADIUS service for returning roles in ACCEPT messages. The roles are returned in the ReplyMessage attribute (type 18). Depending on the configuration, each role is returned in a separate attribute or all roles are concatenated and returned in a single attribute (see property <i>Separator</i>).",
   displayName = "RADIUS Roles As Reply-Message"
)
@PluginLicenseRestriction(moduleAffiliation = "RadiusServer")
public final class RadiusRolesAsReplyMessageConfiguration implements RadiusRolesConfiguration {
   private static final ELogger LOG = ELoggerFactory.getLogger(RadiusRolesAsReplyMessageConfiguration.class);
   @BooleanProperty(
      description = "Add the user's granted roles to the list of returned roles. If disabled, only static roles are returned.",
      defaultValue = "true",
      optional = true
   )
   private boolean returnGrantedUserRoles;
   @StringListProperty(
      description = "Additional static roles that are added to the list of returned roles.",
      optional = true,
      exampleElements = {"role1", "role2", "admin"}
   )
   private List<String> staticRoles;
   @BooleanProperty(
      description = "If this flag is enabled, the original human-readable reply message sent with ACCEPT messages is suppressed. That is, only the roles are returned. If the option is disabled, roles are added as additional attributes after the original message.",
      defaultValue = "true",
      optional = true
   )
   private boolean suppressOriginalReplyMessage;
   @StringProperty(
      description = "<p>If a separator is defined, all roles are concatenated using the defined separator. The resulting list of roles is returned in a <i>single</i> ReplyMessage attribute. If no separator is defined, each role is returned in a <i>separate</i> ReplyMessage attribute.</p><p>For example, assume a user has roles RA, RB and RC. Defining the separator to be \"--\" results in the ReplyMessage \"RA--RB--RC\"</p>",
      optional = true,
      exampleValues = {";", ",", "--"},
      maxLength = 3
   )
   private String separator;

   @Override
   public void addRolesToRadiusResponse(String username, String[] grantedRoles, Optional<String> message, Consumer<IamRadiusServerAttribute> destination) {
      if (!this.suppressOriginalReplyMessage) {
         addReplyMessage(destination, message);
      }

      Set<String> allRoles = new HashSet<>();
      if (this.returnGrantedUserRoles && grantedRoles != null) {
         allRoles.addAll(List.of(grantedRoles));
      }

      allRoles.addAll(this.staticRoles);
      if (StringUtils.isEmpty(this.separator)) {
         allRoles.forEach(r -> addReplyMessage(destination, r));
      } else {
         addReplyMessage(destination, StringUtils.join(allRoles, this.separator));
      }

      LOG.info(
         LogTypes.SERVER_INFO_SYS, "Returning the following roles for user " + LogUtil.quote(username) + " in response: " + StringUtils.join(allRoles, ", ")
      );
   }

   private static void addReplyMessage(Consumer<IamRadiusServerAttribute> destination, Optional<String> message) {
      message.ifPresent(m -> addReplyMessage(destination, m));
   }

   private static void addReplyMessage(Consumer<IamRadiusServerAttribute> destination, String message) {
      destination.accept(IamRadiusServerContext.createReplyMessageAttribute(message));
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public RadiusRolesAsReplyMessageConfiguration(
      boolean returnGrantedUserRoles, List<String> staticRoles, boolean suppressOriginalReplyMessage, String separator
   ) {
      this.returnGrantedUserRoles = returnGrantedUserRoles;
      this.staticRoles = staticRoles;
      this.suppressOriginalReplyMessage = suppressOriginalReplyMessage;
      this.separator = separator;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class RadiusRolesAsReplyMessageConfigurationBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private boolean returnGrantedUserRoles;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> staticRoles;
      @SuppressFBWarnings(justification = "generated code")
      private boolean suppressOriginalReplyMessage;
      @SuppressFBWarnings(justification = "generated code")
      private String separator;

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusRolesAsReplyMessageConfiguration.RadiusRolesAsReplyMessageConfigurationBuilder returnGrantedUserRoles(boolean returnGrantedUserRoles) {
         this.returnGrantedUserRoles = returnGrantedUserRoles;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusRolesAsReplyMessageConfiguration.RadiusRolesAsReplyMessageConfigurationBuilder suppressOriginalReplyMessage(
         boolean suppressOriginalReplyMessage
      ) {
         this.suppressOriginalReplyMessage = suppressOriginalReplyMessage;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusRolesAsReplyMessageConfiguration.RadiusRolesAsReplyMessageConfigurationBuilder separator(String separator) {
         this.separator = separator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusRolesAsReplyMessageConfiguration build() {
         return new RadiusRolesAsReplyMessageConfiguration(this.returnGrantedUserRoles, this.staticRoles, this.suppressOriginalReplyMessage, this.separator);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "RadiusRolesAsReplyMessageConfiguration.RadiusRolesAsReplyMessageConfigurationBuilder(returnGrantedUserRoles="
            + this.returnGrantedUserRoles
            + ", staticRoles="
            + this.staticRoles
            + ", suppressOriginalReplyMessage="
            + this.suppressOriginalReplyMessage
            + ", separator="
            + this.separator
            + ")";
      }
   }
}
