package com.airlock.iam.oauth2.domain.model.client.communication;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.misc.json.JsonCoding;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.login.api.domain.model.oauth2.AccessToken;
import com.airlock.iam.oauth2.domain.model.OAuth2Param;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
import lombok.NonNull;
import org.apache.commons.lang3.StringUtils;

public class OAuth2ClientParserImpl implements OAuth2ClientParser {
   @NonNull
   private final ELogger logger;

   @Override
   public Map<String, Object> parseAccessTokenResponse(String accessTokenResponseBody) {
      Optional<Map<String, Object>> optionalAccessTokenResponse = JsonCoding.tryMapFromJson(accessTokenResponseBody);
      if (optionalAccessTokenResponse.isEmpty()) {
         this.logger.info("Error during OAuth 2.0 SSO: Could not parse Access Token due to invalid JSON syntax.");
         return Map.of();
      } else {
         Map<String, Object> parsedAccessTokenResponse = optionalAccessTokenResponse.get();
         if (parsedAccessTokenResponse.get(OAuth2Param.ACCESS_TOKEN.getParameterName()) == null) {
            this.logger.info("Error during OAuth 2.0 SSO: Access Token not found!");
            return Map.of();
         } else if (StringUtils.isBlank(parsedAccessTokenResponse.get(OAuth2Param.ACCESS_TOKEN.getParameterName()).toString())) {
            this.logger.info("Error during OAuth 2.0 SSO: Access Token is blank!");
            return Map.of();
         } else {
            return parsedAccessTokenResponse;
         }
      }
   }

   @Override
   public Optional<AccessToken> extractAccessToken(Map<String, Object> parsedAccessTokenResponse) {
      if (!parsedAccessTokenResponse.containsKey(OAuth2Param.ACCESS_TOKEN.getParameterName())) {
         this.logger.info("Response does not contain an access token.");
         return Optional.empty();
      } else {
         Object accessTokenObject = parsedAccessTokenResponse.get(OAuth2Param.ACCESS_TOKEN.getParameterName());
         if (!(accessTokenObject instanceof String)) {
            this.logger.info(String.format("Access token is not of type String but is of type '%s'.", accessTokenObject.getClass().getName()));
            return Optional.empty();
         } else {
            String accessToken = (String)parsedAccessTokenResponse.get(OAuth2Param.ACCESS_TOKEN.getParameterName());
            if (StringUtils.isBlank(accessToken)) {
               this.logger.info("Access token does not contain a value.");
               return Optional.empty();
            } else {
               return Optional.of(new AccessToken(accessToken));
            }
         }
      }
   }

   @Override
   public URI parsePushedAuthorizationRequestRequestUri(String pushedAuthorizationRequestResponseBody) {
      return JsonCoding.tryFromJsonToMapOf(pushedAuthorizationRequestResponseBody, String.class)
         .map(m -> m.get(OAuth2Param.REQUEST_URI.getParameterName()))
         .map(URI::create)
         .orElseThrow(() -> new FailureException("Could not parse OAuth 2.0 Pushed Authorization Request response body"));
   }

   @SuppressFBWarnings(justification = "generated code")
   public static OAuth2ClientParserImpl.OAuth2ClientParserImplBuilder builder() {
      return new OAuth2ClientParserImpl.OAuth2ClientParserImplBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2ClientParserImpl(@NonNull ELogger logger) {
      if (logger == null) {
         throw new NullPointerException("logger is marked non-null but is null");
      }

      this.logger = logger;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   protected ELogger getLogger() {
      return this.logger;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2ClientParserImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ELogger logger;

      @SuppressFBWarnings(justification = "generated code")
      OAuth2ClientParserImplBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientParserImpl.OAuth2ClientParserImplBuilder logger(@NonNull ELogger logger) {
         if (logger == null) {
            throw new NullPointerException("logger is marked non-null but is null");
         }

         this.logger = logger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientParserImpl build() {
         return new OAuth2ClientParserImpl(this.logger);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ClientParserImpl.OAuth2ClientParserImplBuilder(logger=" + this.logger + ")";
      }
   }
}
