package com.airlock.iam.login.domain.model.location.propagate;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.location.transform.TransformationResult;
import com.airlock.iam.common.domain.model.transform.TransformationProcessor;
import com.airlock.iam.core.misc.authen.LogTypes;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.login.application.configuration.location.propagate.TargetURIResolverConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;

@LogicForConfig(TargetURIResolverConfig.class)
public class TargetURIResolver {
   private static final ELogger LOG = ELoggerFactory.getLogger(TargetURIResolver.class);
   private final URI defaultValue;
   private final List<Pattern> allowedURIs;
   private final TransformationProcessor<URI> processor;

   @Inject
   public TargetURIResolver(URI defaultValue, List<Pattern> allowedURIs, TransformationProcessor<URI> processor) {
      this.defaultValue = defaultValue;
      this.allowedURIs = allowedURIs;
      this.processor = processor;
   }

   public URI resolveTargetUri(Optional<URI> location) {
      return this.transform(this.ifAllowedElseDefault(location));
   }

   private URI ifAllowedElseDefault(Optional<URI> location) {
      if (location.isPresent() && this.isURIAllowed(location.get())) {
         return location.get();
      }

      if (location.isPresent()) {
         LOG.info("Location " + LogUtil.logSafeQuote(location.get()) + " is not allowed, falling back to default location " + LogUtil.quote(this.defaultValue));
      } else {
         LOG.info("No location was provided, using default location " + LogUtil.quote(this.defaultValue));
      }

      return this.defaultValue;
   }

   private boolean isURIAllowed(URI location) {
      if (StringUtils.isNotEmpty(location.getUserInfo())) {
         LOG.warn(LogTypes.URL_DISALLOWED_SAT, "Rejecting forward location URI containing user information");
         LOG.debug("Rejecting forward location URI containing user information: " + LogUtil.logSafeQuote(location));
         return false;
      } else {
         return this.allowedURIs.stream().anyMatch(it -> it.matcher(location.toString()).matches());
      }
   }

   private URI transform(URI uri) {
      TransformationResult<URI> result = this.processor.transformToResult(uri);
      return result.isVeto() ? this.defaultValue : result.getValue();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class TargetURIResolverBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private URI defaultValue;
      @SuppressFBWarnings(justification = "generated code")
      private List<Pattern> allowedURIs;
      @SuppressFBWarnings(justification = "generated code")
      private TransformationProcessor<URI> processor;

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

      @SuppressFBWarnings(justification = "generated code")
      public TargetURIResolver.TargetURIResolverBuilder defaultValue(URI defaultValue) {
         this.defaultValue = defaultValue;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetURIResolver.TargetURIResolverBuilder allowedURIs(List<Pattern> allowedURIs) {
         this.allowedURIs = allowedURIs;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetURIResolver.TargetURIResolverBuilder processor(TransformationProcessor<URI> processor) {
         this.processor = processor;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetURIResolver build() {
         return new TargetURIResolver(this.defaultValue, this.allowedURIs, this.processor);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "TargetURIResolver.TargetURIResolverBuilder(defaultValue="
            + this.defaultValue
            + ", allowedURIs="
            + this.allowedURIs
            + ", processor="
            + this.processor
            + ")";
      }
   }
}
