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

import com.airlock.iam.common.api.domain.model.authentication.Authentee;
import com.airlock.iam.core.misc.authorization.AccessController;
import com.airlock.iam.core.misc.authorization.AccessControllerException;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Plugin(
   description = "A role-based access controller that provides access to resources based on URL path patterns.",
   displayName = "Resource Access Controller"
)
public final class ResourceAccessController implements AccessController {
   public static final String RESOURCE_ACCESS_RULE_DISPLAY_NAME = "Resource Access Rule";
   public static final String RESOURCE_ACCESS_CONTROLLER_DISPLAY_NAME = "Resource Access Controller";
   static final String NO_RESTRICTION = "NO RESTRICTION";
   static final ELogger LOG = ELoggerFactory.getLogger(ResourceAccessController.class);
   @PluginListProperty(description = "List of access rules defining required roles per resource pattern.", optional = false, displayName = "Allow Rules")
   private List<ResourceAccessControllerAccessRule> allowRules;
   public static final String ALLOW_RULES_DISPLAY_NAME = "Allow Rules";

   @Override
   public boolean isActionDefined(String action) {
      for (ResourceAccessControllerAccessRule rule : this.allowRules) {
         if (rule.isApplicableTo(action)) {
            return true;
         }
      }

      return false;
   }

   @Override
   public boolean isActionAllowedFor(String action, Authentee subject) throws AccessControllerException {
      for (ResourceAccessControllerAccessRule rule : this.allowRules) {
         if (rule.allows(action, subject)) {
            return true;
         }
      }

      return false;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public ResourceAccessController.ResourceAccessControllerBuilder toBuilder() {
      ResourceAccessController.ResourceAccessControllerBuilder builder = new ResourceAccessController.ResourceAccessControllerBuilder();
      if (this.allowRules != null) {
         builder.allowRules(this.allowRules);
      }

      return builder;
   }

   @SuppressFBWarnings(justification = "generated code")
   public ResourceAccessController(List<ResourceAccessControllerAccessRule> allowRules) {
      this.allowRules = allowRules;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class ResourceAccessControllerBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<ResourceAccessControllerAccessRule> allowRules;

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

      @SuppressFBWarnings(justification = "generated code")
      public ResourceAccessController.ResourceAccessControllerBuilder allowRule(ResourceAccessControllerAccessRule allowRule) {
         if (this.allowRules == null) {
            this.allowRules = new ArrayList<>();
         }

         this.allowRules.add(allowRule);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ResourceAccessController.ResourceAccessControllerBuilder allowRules(Collection<? extends ResourceAccessControllerAccessRule> allowRules) {
         if (allowRules == null) {
            throw new NullPointerException("allowRules cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public ResourceAccessController.ResourceAccessControllerBuilder clearAllowRules() {
         if (this.allowRules != null) {
            this.allowRules.clear();
         }

         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "ResourceAccessController.ResourceAccessControllerBuilder(allowRules=" + this.allowRules + ")";
      }
   }
}
