package com.airlock.iam.common.application.configuration.context;

import com.airlock.iam.core.misc.util.ip.IpRestrictionHelper;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.lifecycle.AfterConfigActivation;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.InitializeException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.exception.PropertyTypeMismatchException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import org.apache.commons.lang3.StringUtils;

@Plugin(displayName = "IP Range Context", description = "An IP address to context name mapping.")
@SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
public class IpRangeContext {
   @StringListProperty(
      displayName = "IP Ranges",
      description = "<p>A list of IP ranges resulting in the specified context if they match the client IP.\nThe following notations of IPs and IP ranges are supported:</p>\n<table>\n<tr><td>Single IPv4:</td><td> xxx.xxx.xxx.xxx</td></tr>\n<tr><td>IPv4-Range (CIDR notation):</td><td> xxx.xxx.xxx.xxx/n</td></tr>\n<tr><td>IPv4-Range (subnet mask):</td><td> xxx.xxx.xxx.xxx/mmm.mmm.mmm.mmm</td></tr>\n</table>",
      optional = true,
      exampleElements = {"192.168.123.4", "192.168.123.0/24", "192.168.123.0/255.255.255.0"}
   )
   private List<String> ipRanges;
   private static final String IP_RANGES_FIELD = "ipRanges";
   @StringProperty(
      description = "The resulting configuration context identifier if the client IP matches the configured IP ranges.<br>\nUse \"[DEFAULT]\" to explicitly return the default context.\n",
      optional = false,
      exampleValues = {"CTX1", "EXT", "[DEFAULT]"}
   )
   private String configurationContext;
   private IpRestrictionHelper ipHelper;

   public IpRangeContext(List<String> ipRanges, String configurationContext) {
      this.ipRanges = ipRanges;
      this.configurationContext = configurationContext;
   }

   @AfterConfigActivation
   void afterConfigActivation() throws InitializeException {
      this.ipHelper = new IpRestrictionHelper();

      for (String ipOrRange : this.ipRanges) {
         try {
            this.ipHelper.addIpOrRange(ipOrRange);
         } catch (IllegalArgumentException e) {
            throw new InitializeException("Invalid syntax for allowed IP address/range: " + ipOrRange, e);
         }
      }
   }

   @ConfigurationValidator
   public static void checkIpRangesSyntax(PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws PropertyConstraintViolationException, PropertyTypeMismatchException {
      List<String> ipOrRanges = pc.getSimpleListPropertyConfigurationValue("ipRanges", env, context);
      if (ipOrRanges != null) {
         IpRestrictionHelper restrictionsHelper = new IpRestrictionHelper();

         for (String ipOrRange : ipOrRanges) {
            if (!StringUtils.isNotBlank(ipOrRange)) {
               throw new PropertyConstraintViolationException("ipRanges", pc, env, context, "Elements must not be blank. ");
            }

            try {
               restrictionsHelper.addIpOrRange(ipOrRange);
            } catch (IllegalArgumentException e) {
               throw new PropertyConstraintViolationException("ipRanges", pc, env, context, "Invalid syntax: " + ipOrRange, e);
            }
         }
      }
   }

   public boolean isIpInRange(String ip) {
      return this.ipHelper.isIpAllowed(ip);
   }

   public String getConfigurationContext() {
      return this.configurationContext;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class IpRangeContextBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private List<String> ipRanges;
      @SuppressFBWarnings(justification = "generated code")
      private String configurationContext;

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

      @SuppressFBWarnings(justification = "generated code")
      public IpRangeContext.IpRangeContextBuilder ipRanges(List<String> ipRanges) {
         this.ipRanges = ipRanges;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public IpRangeContext.IpRangeContextBuilder configurationContext(String configurationContext) {
         this.configurationContext = configurationContext;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public IpRangeContext build() {
         return new IpRangeContext(this.ipRanges, this.configurationContext);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "IpRangeContext.IpRangeContextBuilder(ipRanges=" + this.ipRanges + ", configurationContext=" + this.configurationContext + ")";
      }
   }
}
