package com.airlock.iam.common.infrastructure.interceptor;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.misc.exception.Exceptions;
import com.airlock.iam.common.api.domain.service.Interceptor;
import com.airlock.iam.common.api.domain.service.exception.InterceptorVetoException;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.function.BiConsumer;

public final class InterceptorManager {
   private static final ELogger LOG = ELoggerFactory.getLogger(InterceptorManager.class);
   private final List<Interceptor> interceptors;

   public <D, I extends Interceptor> void runOperation(Class<I> interceptorType, D data, BiConsumer<D, I> operation) {
      for (Interceptor interceptor : this.interceptors) {
         if (interceptorType.isAssignableFrom(interceptor.getClass())) {
            try {
               operation.accept(data, interceptorType.cast(interceptor));
            } catch (InterceptorVetoException e) {
               LOG.info(String.format("Interceptor '%s' has vetoed operation execution.", interceptorType.getSimpleName()));
               throw new FailureException(e);
            } catch (RuntimeException e) {
               LOG.error(EDefaultLogType.GENERIC_LOG_TYPE, String.format("Interceptor '%s' has thrown unexpected exception.", interceptorType.getSimpleName()));
               throw (FailureException)Exceptions.findCause(e, FailureException.class).orElse(new FailureException(e));
            }
         }
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public InterceptorManager(List<Interceptor> interceptors) {
      this.interceptors = interceptors;
   }
}
