package com.airlock.iam.oauth2.infrastructure.resource.rest;

import com.airlock.iam.oauth2.domain.model.validation.OAuth2Error;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2ValidationResult;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.nimbusds.oauth2.sdk.token.BearerTokenError;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.Response.Status;
import java.util.Optional;

public final class OAuth2ErrorResponse {
   private final Status status;
   private final Optional<String> authenticateHeader;
   private final Optional<OAuth2ErrorResponse.ErrorData> errorData;

   public static OAuth2ErrorResponse map(OAuth2ValidationResult validationResult) {
      switch (validationResult.getStatus()) {
         case REQUEST_NOT_VALID:
            return new OAuth2ErrorResponse(
               Status.BAD_REQUEST, Optional.empty(), Optional.of(errorData(OAuth2Error.INVALID_REQUEST, validationResult.getDescription()))
            );
         case BEARER_TOKEN_REQUIRED:
            return new OAuth2ErrorResponse(
               Status.UNAUTHORIZED,
               Optional.of(authenticateHeader(BearerTokenError.MISSING_TOKEN)),
               Optional.of(errorData(OAuth2Error.INVALID_CLIENT, validationResult.getDescription()))
            );
         case BEARER_TOKEN_NOT_VALID:
         case CLIENT_NOT_FOUND:
         case CLIENT_LOCKED:
         case USER_NOT_VALID:
            return new OAuth2ErrorResponse(
               Status.UNAUTHORIZED,
               Optional.of(authenticateHeader(BearerTokenError.INVALID_TOKEN)),
               Optional.of(errorData(OAuth2Error.INVALID_CLIENT, validationResult.getDescription()))
            );
         case SCOPES_REQUIRED:
            return new OAuth2ErrorResponse(
               Status.FORBIDDEN,
               Optional.of(authenticateHeader(BearerTokenError.INSUFFICIENT_SCOPE)),
               Optional.of(errorData(OAuth2Error.INSUFFICIENT_SCOPE, validationResult.getDescription()))
            );
         case BASIC_AUTH_NOT_VALID:
            return new OAuth2ErrorResponse(
               Status.UNAUTHORIZED, Optional.empty(), Optional.of(errorData(OAuth2Error.INVALID_CLIENT, validationResult.getDescription()))
            );
         case UNEXPECTED:
         default:
            return new OAuth2ErrorResponse(Status.INTERNAL_SERVER_ERROR, Optional.empty(), Optional.empty());
      }
   }

   private static String authenticateHeader(BearerTokenError error) {
      return error.toWWWAuthenticateHeader();
   }

   private static OAuth2ErrorResponse.ErrorData errorData(OAuth2Error error, Optional<String> description) {
      return new OAuth2ErrorResponse.ErrorData(error.getParameterValue(), description.orElse(null));
   }

   public Response build() {
      ResponseBuilder response = Response.status(this.status).type(MediaType.APPLICATION_JSON_TYPE);
      this.authenticateHeader.ifPresent(header -> response.header("WWW-Authenticate", header));
      this.errorData.ifPresent(response::entity);
      return response.build();
   }

   @SuppressFBWarnings(justification = "generated code")
   private OAuth2ErrorResponse(Status status, Optional<String> authenticateHeader, Optional<OAuth2ErrorResponse.ErrorData> errorData) {
      this.status = status;
      this.authenticateHeader = authenticateHeader;
      this.errorData = errorData;
   }

   private static final class ErrorData {
      @JsonProperty("error")
      private final String error;
      @JsonProperty("error_description")
      private final String description;

      @SuppressFBWarnings(justification = "generated code")
      public ErrorData(String error, String description) {
         this.error = error;
         this.description = description;
      }

      @SuppressFBWarnings(justification = "generated code")
      public String getError() {
         return this.error;
      }

      @SuppressFBWarnings(justification = "generated code")
      public String getDescription() {
         return this.description;
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public boolean equals(Object o) {
         if (o == this) {
            return true;
         } else if (!(o instanceof OAuth2ErrorResponse.ErrorData other)) {
            return false;
         } else {
            Object this$error = this.error;
            Object other$error = other.error;
            if (this$error == null ? other$error == null : this$error.equals(other$error)) {
               Object this$description = this.description;
               Object other$description = other.description;
               return this$description == null ? other$description == null : this$description.equals(other$description);
            } else {
               return false;
            }
         }
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public int hashCode() {
         int PRIME = 59;
         int result = 1;
         Object $error = this.error;
         result = result * 59 + ($error == null ? 43 : $error.hashCode());
         Object $description = this.description;
         return result * 59 + ($description == null ? 43 : $description.hashCode());
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ErrorResponse.ErrorData(error=" + this.getError() + ", description=" + this.getDescription() + ")";
      }
   }
}
