package com.airlock.iam.authentication.infrastructure.service.oneshot;

import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.domain.model.step.StepResultData;
import com.airlock.iam.flow.domain.model.result.NotAuthorizedData;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;
import java.util.List;

public final class OneShotResponseService {
   private static final ELogger LOG = ELoggerFactory.getLogger(OneShotResponseService.class);
   private final List<OneShotResponseMapper> mappers;
   private final OneShotResponseMapper notAuthorizedMapper;
   private final OneShotResponseMapper forbiddenMapper;

   public Response createResponse(FlowResult flowResult) {
      if (flowResult.flowCompletedSuccessfully()) {
         return Response.ok().build();
      }

      ResponseBuilder builder = this.mappers
         .stream()
         .filter(m -> m.matches(flowResult))
         .findFirst()
         .map(m -> mapResponse(flowResult, m))
         .orElseGet(() -> this.createDefaultResponse(flowResult));
      flowResult.<StepResultData>data()
         .filter(NotAuthorizedData.class::isInstance)
         .map(NotAuthorizedData.class::cast)
         .ifPresent(d -> builder.header("WWW-Authenticate", d.wwwAuthenticate()));
      return builder.build();
   }

   private ResponseBuilder createDefaultResponse(FlowResult flowResult) {
      return hasNotAuthorizedData(flowResult) ? mapResponse(flowResult, this.notAuthorizedMapper) : mapResponse(flowResult, this.forbiddenMapper);
   }

   private static ResponseBuilder mapResponse(FlowResult flowResult, OneShotResponseMapper mapper) {
      LOG.info(
         "Mapping flow result "
            + flowResult.error().map(e -> "with error " + e).orElse("without error")
            + " and "
            + flowResult.nextStep().map(s -> "next step " + s).orElse("without next step")
            + " to response with status "
            + mapper.getHttpStatusCode()
            + "."
      );
      return mapper.createResponse();
   }

   private static boolean hasNotAuthorizedData(FlowResult flowResult) {
      return flowResult.data().isPresent() && flowResult.data().get() instanceof NotAuthorizedData;
   }

   @SuppressFBWarnings(justification = "generated code")
   public OneShotResponseService(List<OneShotResponseMapper> mappers, OneShotResponseMapper notAuthorizedMapper, OneShotResponseMapper forbiddenMapper) {
      this.mappers = mappers;
      this.notAuthorizedMapper = notAuthorizedMapper;
      this.forbiddenMapper = forbiddenMapper;
   }
}
