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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.infrastructure.uri.ExternalUriInfo;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import java.util.Optional;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.jersey.spi.ExceptionMappers;

@jakarta.ws.rs.ext.Provider
public class MultiExceptionMapper extends BaseExceptionMapper<MultiException> {
   private final Provider<ExceptionMappers> exceptionMappers;

   @Inject
   public MultiExceptionMapper(Provider<ExceptionMappers> exceptionMappers, Provider<ExternalUriInfo> uriInfo) {
      super(Status.INTERNAL_SERVER_ERROR, uriInfo);
      this.exceptionMappers = exceptionMappers;
   }

   public Response toResponse(MultiException exception) {
      Throwable cause = exception.getCause();
      return this.findExceptionMapper(cause).map(mapper -> mapper.toResponse(cause)).orElseGet(() -> super.toResponse(exception));
   }

   protected boolean shouldLogStacktraceAsError(MultiException exception) {
      return !this.hasExceptionMapperForCause(exception) || !this.containsIamExceptionAsCause(exception);
   }

   private boolean hasExceptionMapperForCause(MultiException exception) {
      return this.findExceptionMapper(exception.getCause()).isPresent();
   }

   private boolean containsIamExceptionAsCause(MultiException exception) {
      return exception.getCause() instanceof FailureException || exception.getCause() instanceof ViolationException;
   }

   private Optional<ExceptionMapper<Throwable>> findExceptionMapper(Throwable throwable) {
      return Optional.ofNullable(((ExceptionMappers)this.exceptionMappers.get()).findMapping(throwable));
   }
}
