package com.airlock.iam.common.infrastructure.restclient;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.Invocation.Builder;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import lombok.NonNull;

public class RestRequest {
   @NonNull
   private final String path;
   @NonNull
   private final RestRequest.RestRequestMethod method;
   private final Map<String, Object> queryValues;
   private final Map<String, List<Object>> headers;
   private final List<Cookie> cookies;
   private final Entity<?> body;

   Invocation toInvocation(Client client, String serverUrl) {
      URI url = this.getUrl(serverUrl);
      Builder request = client.target(url).request();
      this.headers.forEach((name, values) -> values.forEach(value -> request.header(name, value)));
      this.cookies.forEach(request::cookie);
      return this.body == null ? request.build(this.method.getMethodName()) : request.build(this.method.getMethodName(), this.body);
   }

   public String getDescription() {
      return this.method + " " + this.path;
   }

   public URI getUrl(String baseUri) {
      UriBuilder uri = UriBuilder.fromUri(baseUri).path(this.path);

      for (Entry<String, Object> entry : this.queryValues.entrySet()) {
         uri = uri.queryParam(entry.getKey(), new Object[]{entry.getValue()});
      }

      return uri.build(new Object[0]);
   }

   @SuppressFBWarnings(justification = "generated code")
   RestRequest(
      @NonNull String path,
      @NonNull RestRequest.RestRequestMethod method,
      Map<String, Object> queryValues,
      Map<String, List<Object>> headers,
      List<Cookie> cookies,
      Entity<?> body
   ) {
      if (path == null) {
         throw new NullPointerException("path is marked non-null but is null");
      }

      if (method == null) {
         throw new NullPointerException("method is marked non-null but is null");
      }

      this.path = path;
      this.method = method;
      this.queryValues = queryValues;
      this.headers = headers;
      this.cookies = cookies;
      this.body = body;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public String toString() {
      return "RestRequest(path="
         + this.getPath()
         + ", method="
         + this.getMethod()
         + ", queryValues="
         + this.getQueryValues()
         + ", headers="
         + this.getHeaders()
         + ", cookies="
         + this.getCookies()
         + ")";
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public String getPath() {
      return this.path;
   }

   @NonNull
   @SuppressFBWarnings(justification = "generated code")
   public RestRequest.RestRequestMethod getMethod() {
      return this.method;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Map<String, Object> getQueryValues() {
      return this.queryValues;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Map<String, List<Object>> getHeaders() {
      return this.headers;
   }

   @SuppressFBWarnings(justification = "generated code")
   public List<Cookie> getCookies() {
      return this.cookies;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Entity<?> getBody() {
      return this.body;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class RestRequestBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String path;
      @SuppressFBWarnings(justification = "generated code")
      private RestRequest.RestRequestMethod method;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> queryValues$key;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Object> queryValues$value;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> headers$key;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<List<Object>> headers$value;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Cookie> cookies;
      @SuppressFBWarnings(justification = "generated code")
      private Entity<?> body;

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

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder path(@NonNull String path) {
         if (path == null) {
            throw new NullPointerException("path is marked non-null but is null");
         }

         this.path = path;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder method(@NonNull RestRequest.RestRequestMethod method) {
         if (method == null) {
            throw new NullPointerException("method is marked non-null but is null");
         }

         this.method = method;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder queryValue(String queryValueKey, Object queryValueValue) {
         if (this.queryValues$key == null) {
            this.queryValues$key = new ArrayList<>();
            this.queryValues$value = new ArrayList<>();
         }

         this.queryValues$key.add(queryValueKey);
         this.queryValues$value.add(queryValueValue);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder queryValues(Map<? extends String, ? extends Object> queryValues) {
         if (queryValues == null) {
            throw new NullPointerException("queryValues cannot be null");
         }

         if (this.queryValues$key == null) {
            this.queryValues$key = new ArrayList<>();
            this.queryValues$value = new ArrayList<>();
         }

         for (Entry<? extends String, ? extends Object> $lombokEntry : queryValues.entrySet()) {
            this.queryValues$key.add($lombokEntry.getKey());
            this.queryValues$value.add($lombokEntry.getValue());
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder clearQueryValues() {
         if (this.queryValues$key != null) {
            this.queryValues$key.clear();
            this.queryValues$value.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder header(String headerKey, List<Object> headerValue) {
         if (this.headers$key == null) {
            this.headers$key = new ArrayList<>();
            this.headers$value = new ArrayList<>();
         }

         this.headers$key.add(headerKey);
         this.headers$value.add(headerValue);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder headers(Map<? extends String, ? extends List<Object>> headers) {
         if (headers == null) {
            throw new NullPointerException("headers cannot be null");
         }

         if (this.headers$key == null) {
            this.headers$key = new ArrayList<>();
            this.headers$value = new ArrayList<>();
         }

         for (Entry<? extends String, ? extends List<Object>> $lombokEntry : headers.entrySet()) {
            this.headers$key.add($lombokEntry.getKey());
            this.headers$value.add((List<Object>)$lombokEntry.getValue());
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder clearHeaders() {
         if (this.headers$key != null) {
            this.headers$key.clear();
            this.headers$value.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder cookie(Cookie cookie) {
         if (this.cookies == null) {
            this.cookies = new ArrayList<>();
         }

         this.cookies.add(cookie);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder cookies(Collection<? extends Cookie> cookies) {
         if (cookies == null) {
            throw new NullPointerException("cookies cannot be null");
         }

         if (this.cookies == null) {
            this.cookies = new ArrayList<>();
         }

         this.cookies.addAll(cookies);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder clearCookies() {
         if (this.cookies != null) {
            this.cookies.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest.RestRequestBuilder body(Entity<?> body) {
         this.body = body;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RestRequest build() {
         Map var4 = switch (this.queryValues$key == null ? 0 : this.queryValues$key.size()) {
            case 0 -> Collections.emptyMap();
            case 1 -> Collections.singletonMap(this.queryValues$key.get(0), this.queryValues$value.get(0));
            default -> {
               Map<String, Object> queryValues = new LinkedHashMap<>(
                  this.queryValues$key.size() < 1073741824 ? 1 + this.queryValues$key.size() + (this.queryValues$key.size() - 3) / 3 : Integer.MAX_VALUE
               );

               for (int $i = 0; $i < this.queryValues$key.size(); $i++) {
                  queryValues.put(this.queryValues$key.get($i), this.queryValues$value.get($i));
               }

               yield Collections.unmodifiableMap(queryValues);
            }
         };

         Map var6 = switch (this.headers$key == null ? 0 : this.headers$key.size()) {
            case 0 -> Collections.emptyMap();
            case 1 -> Collections.singletonMap(this.headers$key.get(0), this.headers$value.get(0));
            default -> {
               Map<String, List<Object>> headers = new LinkedHashMap<>(
                  this.headers$key.size() < 1073741824 ? 1 + this.headers$key.size() + (this.headers$key.size() - 3) / 3 : Integer.MAX_VALUE
               );

               for (int $i = 0; $i < this.headers$key.size(); $i++) {
                  headers.put(this.headers$key.get($i), this.headers$value.get($i));
               }

               yield Collections.unmodifiableMap(headers);
            }
         };

         return new RestRequest(this.path, this.method, var4, var6, switch (this.cookies == null ? 0 : this.cookies.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.cookies.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.cookies));
         }, this.body);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "RestRequest.RestRequestBuilder(path="
            + this.path
            + ", method="
            + this.method
            + ", queryValues$key="
            + this.queryValues$key
            + ", queryValues$value="
            + this.queryValues$value
            + ", headers$key="
            + this.headers$key
            + ", headers$value="
            + this.headers$value
            + ", cookies="
            + this.cookies
            + ", body="
            + this.body
            + ")";
      }
   }

   public enum RestRequestMethod {
      GET("GET"),
      POST("POST"),
      PUT("PUT"),
      PATCH("PATCH"),
      DELETE("DELETE");

      private final String methodName;

      @Override
      public String toString() {
         return this.methodName;
      }

      @SuppressFBWarnings(justification = "generated code")
      RestRequestMethod(final String methodName) {
         this.methodName = methodName;
      }

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