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

import com.airlock.iam.base.domain.service.uri.IamUriBuilder;
import com.airlock.iam.base.domain.service.uri.UrlUtils;
import com.airlock.iam.oauth2.domain.model.FormPostParameters;
import com.airlock.iam.oauth2.domain.model.OAuth2Param;
import com.airlock.iam.oauth2.domain.model.OAuth2Response;
import com.airlock.iam.oauth2.domain.model.OAuth2ResponseMode;
import com.google.common.annotations.VisibleForTesting;
import java.net.URI;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class OAuth2ResponseBuilder {
   private final URI baseUri;
   private final Map<String, String> parameters;
   private final OAuth2ResponseMode responseMode;

   public OAuth2ResponseBuilder(URI baseUri, OAuth2ResponseMode responseMode) {
      this.baseUri = baseUri;
      this.parameters = new LinkedHashMap<>();
      this.responseMode = responseMode;
   }

   public OAuth2ResponseBuilder addSessionState(String value) {
      this.addParameter(OAuth2Param.SESSION_STATE.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addError(String value) {
      this.addParameter(OAuth2Param.ERROR.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addErrorDescription(String value) {
      this.addParameter(OAuth2Param.ERROR_DESCRIPTION.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addState(String value) {
      this.addParameter(OAuth2Param.STATE.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addIdToken(String value) {
      this.addParameter(OAuth2Param.ID_TOKEN.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addAccessToken(String value) {
      this.addParameter(OAuth2Param.ACCESS_TOKEN.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addTokenType(String value) {
      this.addParameter(OAuth2Param.TOKEN_TYPE.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addExpiresIn(String value) {
      this.addParameter(OAuth2Param.EXPIRES_IN.getParameterName(), value);
      return this;
   }

   public OAuth2ResponseBuilder addAuthzCode(String value) {
      this.addParameter(OAuth2Param.AUTHZ_CODE.getParameterName(), value);
      return this;
   }

   @VisibleForTesting
   OAuth2ResponseBuilder addParameter(String name, String value) {
      this.parameters.put(name, value);
      return this;
   }

   public OAuth2Response buildResponse() {
      Optional<String> error = Optional.ofNullable(this.parameters.get(OAuth2Param.ERROR.getParameterName()));
      Optional<String> errorDescription = Optional.ofNullable(this.parameters.get(OAuth2Param.ERROR_DESCRIPTION.getParameterName()));

      return switch (this.responseMode) {
         case QUERY_STRING -> new OAuth2Response(error, errorDescription, this.getQueryStringParametersUri(), Optional.empty());
         case FRAGMENT -> new OAuth2Response(error, errorDescription, this.getFragmentParametersUri(), Optional.empty());
         case FORM_POST -> new OAuth2Response(error, errorDescription, this.baseUri, Optional.of(new FormPostParameters(this.parameters)));
      };
   }

   private URI getQueryStringParametersUri() {
      IamUriBuilder builder = IamUriBuilder.fromUri(this.baseUri);
      IamUriBuilder var2 = builder;
      this.parameters.forEach((x$0, xva$1) -> var2.queryParam(x$0, xva$1));
      return builder.build();
   }

   private URI getFragmentParametersUri() {
      return this.parameters.isEmpty()
         ? this.baseUri
         : IamUriBuilder.fromUri(this.baseUri)
            .fragment(
               this.parameters
                  .entrySet()
                  .stream()
                  .map(entry -> UrlUtils.urlEncodeUrlParameter(entry.getKey()) + "=" + UrlUtils.urlEncodeUrlParameter(entry.getValue()))
                  .collect(Collectors.joining("&"))
            )
            .build();
   }
}
