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

import com.airlock.iam.common.domain.model.QueryString;
import com.airlock.iam.oauth2.domain.model.State;
import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse;
import com.nimbusds.oauth2.sdk.AuthorizationResponse;
import com.nimbusds.oauth2.sdk.AuthorizationSuccessResponse;
import com.nimbusds.oauth2.sdk.ErrorObject;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.Response;
import com.nimbusds.oauth2.sdk.id.Identifier;
import com.nimbusds.oauth2.sdk.util.URLUtils;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public final class OAuth2AuthorizationResponse {
   private final AuthorizationResponse response;

   private OAuth2AuthorizationResponse(AuthorizationResponse response) {
      this.response = response;
   }

   public static OAuth2AuthorizationResponse oAuth2AuthorizationResponse(QueryString queryString) throws OAuth2ParseException {
      return parseAuthorizationResponse(URLUtils.parseParameters(queryString.getValue()));
   }

   private static OAuth2AuthorizationResponse parseAuthorizationResponse(Map<String, List<String>> queryParams) throws OAuth2ParseException {
      try {
         AuthorizationResponse response = AuthorizationResponse.parse(URI.create("oauth2-client"), queryParams);
         return new OAuth2AuthorizationResponse(response);
      } catch (ParseException | IllegalArgumentException e) {
         throw new OAuth2ParseException(String.format("Error parsing OAuth 2.0 authorization response: %s", e.getMessage()), e);
      }
   }

   public Optional<OAuth2AuthorizationSuccessResponse> successResponse() {
      return Optional.of(this.response)
         .filter(Response::indicatesSuccess)
         .<AuthorizationSuccessResponse>map(AuthorizationResponse::toSuccessResponse)
         .map(
            nimbusResponse -> new OAuth2AuthorizationSuccessResponse(getState(nimbusResponse).orElse(null), getAuthorizationCode(nimbusResponse).orElse(null))
         );
   }

   public Optional<OAuth2AuthorizationErrorResponse> errorResponse() {
      return this.nimbusErrorResponse().map(nimbusResponse -> mapErrorObjectToResponse(nimbusResponse.getErrorObject()));
   }

   private static OAuth2AuthorizationErrorResponse mapErrorObjectToResponse(ErrorObject obj) {
      Optional<ErrorObject> errorObject = Optional.ofNullable(obj);
      return new OAuth2AuthorizationErrorResponse(
         errorObject.<String>map(ErrorObject::getCode).orElse(null), errorObject.<String>map(ErrorObject::getDescription).orElse(null)
      );
   }

   private Optional<AuthorizationErrorResponse> nimbusErrorResponse() {
      return Optional.of(this.response).filter(authorizationResponse -> !authorizationResponse.indicatesSuccess()).map(AuthorizationResponse::toErrorResponse);
   }

   private static Optional<State> getState(AuthorizationSuccessResponse nimbusResponse) {
      return Optional.ofNullable(nimbusResponse.getState()).<String>map(Identifier::getValue).map(State::state);
   }

   private static Optional<OAuth2AuthorizationSuccessResponse.OAuth2AuthorizationCode> getAuthorizationCode(AuthorizationSuccessResponse nimbusResponse) {
      return Optional.ofNullable(nimbusResponse.getAuthorizationCode())
         .<String>map(Identifier::getValue)
         .map(OAuth2AuthorizationSuccessResponse.OAuth2AuthorizationCode::oAuth2AuthorizationCode);
   }
}
