package com.airlock.iam.admin.application.service.access;

import com.airlock.iam.admin.application.configuration.ContextDataAccessRule;
import com.airlock.iam.admin.application.configuration.UserManagementExtensionAccessRuleConfig;
import com.airlock.iam.admin.domain.model.administrator.permissions.Actions;
import com.airlock.iam.admin.domain.model.administrator.permissions.AdminappAction;
import com.airlock.iam.admin.domain.model.contextdata.AdminRealmContextDataName;
import com.airlock.iam.admin.domain.model.contextdata.UserRealmContextDataName;
import com.airlock.iam.admin.domain.service.access.AdminappAccessController;
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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

public class ActionAccessController implements AdminappAccessController {
   private static final ELogger LOG = ELoggerFactory.getLogger(ActionAccessController.class);
   private AccessController accessController;
   private Authentee authentee;
   private Optional<UserRealmContextDataName> userRealmContextDataName;
   private Optional<AdminRealmContextDataName> adminRealmContextDataName;

   @Override
   public boolean isActionAllowed(AdminappAction action) {
      try {
         return this.accessController.isActionAllowedFor(action.name(), this.authentee);
      } catch (AccessControllerException e) {
         LOG.debug("Access controller caused exception. Denying action '" + action.name() + "'.", e);
         return false;
      }
   }

   @Override
   public boolean isContextDataColumnVisible(String contextFieldProperty) {
      try {
         String columnVisibleAction = ContextDataAccessRule.toAction(Actions.viewContextData, contextFieldProperty);
         return !this.accessController.isActionDefined(columnVisibleAction)
            ? this.isActionAllowed(Actions.viewContextData)
            : this.accessController.isActionAllowedFor(columnVisibleAction, this.authentee);
      } catch (AccessControllerException e) {
         LOG.debug("Access controller caused exception. Denying action on context data field '" + contextFieldProperty + "'.", e);
         return false;
      }
   }

   @Override
   public boolean isContextDataColumnEditable(String contextFieldProperty) {
      try {
         if (this.isRealmAdmin() && this.isUserRealmContextData(contextFieldProperty)) {
            return false;
         }

         if (!this.isContextDataColumnVisible(contextFieldProperty)) {
            return false;
         }

         String columnEditableAction = ContextDataAccessRule.toAction(Actions.editUser, contextFieldProperty);
         return !this.accessController.isActionDefined(columnEditableAction)
            ? this.isActionAllowed(Actions.editUser)
            : this.accessController.isActionAllowedFor(columnEditableAction, this.authentee);
      } catch (AccessControllerException e) {
         LOG.debug("Access controller caused exception. Denying action on context data field '" + contextFieldProperty + "'.", e);
         return false;
      }
   }

   @Override
   public boolean isUserManagementExtensionDisplayed(String userManagementExtensionId) {
      try {
         String displayIdAction = UserManagementExtensionAccessRuleConfig.toAction(userManagementExtensionId);
         return !this.accessController.isActionDefined(displayIdAction)
            ? this.isActionAllowed(Actions.displayUserManagementExtensions)
            : this.accessController.isActionAllowedFor(displayIdAction, this.authentee);
      } catch (AccessControllerException e) {
         LOG.debug("Access controller caused exception. Denying action on User Management Extension with id '" + userManagementExtensionId + "'.", e);
         return false;
      }
   }

   private boolean isUserRealmContextData(String contextFieldProperty) {
      return this.userRealmContextDataName
         .map(UserRealmContextDataName::getContextDataName)
         .filter(contextDataName -> contextDataName.equals(contextFieldProperty))
         .isPresent();
   }

   private boolean isRealmAdmin() {
      return this.adminRealmContextDataName.filter(contextDataName -> contextDataName.hasValueIn(this.authentee.contextData().asKeyValueMap())).isPresent();
   }

   @SuppressFBWarnings(justification = "generated code")
   ActionAccessController(
      AccessController accessController,
      Authentee authentee,
      Optional<UserRealmContextDataName> userRealmContextDataName,
      Optional<AdminRealmContextDataName> adminRealmContextDataName
   ) {
      this.accessController = accessController;
      this.authentee = authentee;
      this.userRealmContextDataName = userRealmContextDataName;
      this.adminRealmContextDataName = adminRealmContextDataName;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class ActionAccessControllerBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private AccessController accessController;
      @SuppressFBWarnings(justification = "generated code")
      private Authentee authentee;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<UserRealmContextDataName> userRealmContextDataName;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<AdminRealmContextDataName> adminRealmContextDataName;

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

      @SuppressFBWarnings(justification = "generated code")
      public ActionAccessController.ActionAccessControllerBuilder accessController(AccessController accessController) {
         this.accessController = accessController;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ActionAccessController.ActionAccessControllerBuilder authentee(Authentee authentee) {
         this.authentee = authentee;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ActionAccessController.ActionAccessControllerBuilder userRealmContextDataName(Optional<UserRealmContextDataName> userRealmContextDataName) {
         this.userRealmContextDataName = userRealmContextDataName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ActionAccessController.ActionAccessControllerBuilder adminRealmContextDataName(Optional<AdminRealmContextDataName> adminRealmContextDataName) {
         this.adminRealmContextDataName = adminRealmContextDataName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ActionAccessController build() {
         return new ActionAccessController(this.accessController, this.authentee, this.userRealmContextDataName, this.adminRealmContextDataName);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "ActionAccessController.ActionAccessControllerBuilder(accessController="
            + this.accessController
            + ", authentee="
            + this.authentee
            + ", userRealmContextDataName="
            + this.userRealmContextDataName
            + ", adminRealmContextDataName="
            + this.adminRealmContextDataName
            + ")";
      }
   }
}
