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

import com.airlock.iam.base.api.infrastructure.uri.ExternalUriInfo;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.core.misc.util.ExceptionUtils;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.saml2.domain.model.Saml2ErrorUri;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import java.util.Optional;
import java.util.stream.Collectors;

@jakarta.ws.rs.ext.Provider
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
   private static final ELogger LOG = ELoggerFactory.getLogger(ValidationExceptionMapper.class);
   private final Provider<Saml2ErrorUri> errorUri;
   private final Provider<ExternalUriInfo> uriInfo;

   @Inject
   public ValidationExceptionMapper(Provider<Saml2ErrorUri> errorUri, Provider<ExternalUriInfo> uriInfo) {
      this.errorUri = errorUri;
      this.uriInfo = uriInfo;
   }

   public Response toResponse(ValidationException exception) {
      LOG.info(
         EDefaultLogType.GENERIC_LOG_TYPE,
         "Invalid request received to "
            + LogUtil.logSafeQuote(((ExternalUriInfo)this.uriInfo.get()).getAbsolutePath())
            + this.getDetails(exception)
            + ". Redirecting to error page."
      );
      return Response.status(Status.FOUND).header("Location", ((Saml2ErrorUri)this.errorUri.get()).getUri()).build();
   }

   private String getDetails(ValidationException exception) {
      return exception instanceof ConstraintViolationException cve
         ? ": " + cve.getConstraintViolations().stream().<CharSequence>map(ConstraintViolation::getMessage).collect(Collectors.joining("; "))
         : Optional.ofNullable(ExceptionUtils.extractCause(exception)).map(message -> ": " + message).orElse("");
   }
}
