package zeenea.connector.openapi.client.model;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import org.immutables.value.Generated;

/**
 * Immutable implementation of {@link Specification}.
 * <p>
 * Use the builder to create immutable instances:
 * {@code ImmutableSpecification.builder()}.
 */
@Generated(from = "Specification", generator = "Immutables")
@SuppressWarnings({"all"})
@SuppressFBWarnings
@ParametersAreNonnullByDefault
@javax.annotation.processing.Generated("org.immutables.processor.ProxyProcessor")
@Immutable
public final class ImmutableSpecification implements Specification {
  private final String version;
  private final Information info;
  private final List<Server> servers;
  private final Map<String, Path> paths;
  private final Components components;

  private ImmutableSpecification(
      String version,
      Information info,
      List<Server> servers,
      Map<String, Path> paths,
      Components components) {
    this.version = version;
    this.info = info;
    this.servers = servers;
    this.paths = paths;
    this.components = components;
  }

  /**
   * @return The value of the {@code version} attribute
   */
  @JsonProperty("openapi")
  @Override
  public String getVersion() {
    return version;
  }

  /**
   * @return The value of the {@code info} attribute
   */
  @JsonProperty("info")
  @Override
  public Information getInfo() {
    return info;
  }

  /**
   * @return The value of the {@code servers} attribute
   */
  @JsonProperty("servers")
  @Override
  public List<Server> getServers() {
    return servers;
  }

  /**
   * @return The value of the {@code paths} attribute
   */
  @JsonProperty("paths")
  @Override
  public Map<String, Path> getPaths() {
    return paths;
  }

  /**
   * @return The value of the {@code components} attribute
   */
  @JsonProperty("components")
  @Override
  public Components getComponents() {
    return components;
  }

  /**
   * Copy the current immutable object by setting a value for the {@link Specification#getVersion() version} attribute.
   * An equals check used to prevent copying of the same value by returning {@code this}.
   * @param value A new value for version
   * @return A modified copy of the {@code this} object
   */
  public final ImmutableSpecification withVersion(String value) {
    String newValue = Objects.requireNonNull(value, "version");
    if (this.version.equals(newValue)) return this;
    return new ImmutableSpecification(newValue, this.info, this.servers, this.paths, this.components);
  }

  /**
   * Copy the current immutable object by setting a value for the {@link Specification#getInfo() info} attribute.
   * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
   * @param value A new value for info
   * @return A modified copy of the {@code this} object
   */
  public final ImmutableSpecification withInfo(Information value) {
    if (this.info == value) return this;
    Information newValue = Objects.requireNonNull(value, "info");
    return new ImmutableSpecification(this.version, newValue, this.servers, this.paths, this.components);
  }

  /**
   * Copy the current immutable object with elements that replace the content of {@link Specification#getServers() servers}.
   * @param elements The elements to set
   * @return A modified copy of {@code this} object
   */
  public final ImmutableSpecification withServers(Server... elements) {
    List<Server> newValue = List.of(elements);
    return new ImmutableSpecification(this.version, this.info, newValue, this.paths, this.components);
  }

  /**
   * Copy the current immutable object with elements that replace the content of {@link Specification#getServers() servers}.
   * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
   * @param elements An iterable of servers elements to set
   * @return A modified copy of {@code this} object
   */
  public final ImmutableSpecification withServers(Iterable<? extends Server> elements) {
    if (this.servers == elements) return this;
    List<Server> newValue = elements instanceof Collection<?>
        ? List.copyOf((Collection<? extends Server>) elements)
        : StreamSupport.stream(elements.spliterator(), false)
            .collect(Collectors.toUnmodifiableList());
    return new ImmutableSpecification(this.version, this.info, newValue, this.paths, this.components);
  }

  /**
   * Copy the current immutable object by replacing the {@link Specification#getPaths() paths} map with the specified map.
   * Nulls are not permitted as keys or values.
   * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
   * @param entries The entries to be added to the paths map
   * @return A modified copy of {@code this} object
   */
  public final ImmutableSpecification withPaths(Map<String, ? extends Path> entries) {
    if (this.paths == entries) return this;
    Map<String, Path> newValue = Map.copyOf(entries);
    return new ImmutableSpecification(this.version, this.info, this.servers, newValue, this.components);
  }

  /**
   * Copy the current immutable object by setting a value for the {@link Specification#getComponents() components} attribute.
   * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}.
   * @param value A new value for components
   * @return A modified copy of the {@code this} object
   */
  public final ImmutableSpecification withComponents(Components value) {
    if (this.components == value) return this;
    Components newValue = Objects.requireNonNull(value, "components");
    return new ImmutableSpecification(this.version, this.info, this.servers, this.paths, newValue);
  }

  /**
   * This instance is equal to all instances of {@code ImmutableSpecification} that have equal attribute values.
   * @return {@code true} if {@code this} is equal to {@code another} instance
   */
  @Override
  public boolean equals(@Nullable Object another) {
    if (this == another) return true;
    return another instanceof ImmutableSpecification
        && equalTo(0, (ImmutableSpecification) another);
  }

  private boolean equalTo(int synthetic, ImmutableSpecification another) {
    return version.equals(another.version)
        && info.equals(another.info)
        && servers.equals(another.servers)
        && paths.equals(another.paths)
        && components.equals(another.components);
  }

  /**
   * Computes a hash code from attributes: {@code version}, {@code info}, {@code servers}, {@code paths}, {@code components}.
   * @return hashCode value
   */
  @Override
  public int hashCode() {
    int h = 5381;
    h += (h << 5) + version.hashCode();
    h += (h << 5) + info.hashCode();
    h += (h << 5) + servers.hashCode();
    h += (h << 5) + paths.hashCode();
    h += (h << 5) + components.hashCode();
    return h;
  }

  /**
   * Prints the immutable value {@code Specification} with attribute values.
   * @return A string representation of the value
   */
  @Override
  public String toString() {
    return "Specification{"
        + "version=" + version
        + ", info=" + info
        + ", servers=" + servers
        + ", paths=" + paths
        + ", components=" + components
        + "}";
  }

  /**
   * Utility type used to correctly read immutable object from JSON representation.
   * @deprecated Do not use this type directly, it exists only for the <em>Jackson</em>-binding infrastructure
   */
  @Generated(from = "Specification", generator = "Immutables")
  @Deprecated
  @JsonDeserialize
  @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
  static final class Json implements Specification {
    @Nullable String version;
    @Nullable Information info;
    @Nullable List<Server> servers = List.of();
    @Nullable Map<String, Path> paths = Map.of();
    @Nullable Components components;
    @JsonProperty("openapi")
    public void setVersion(String version) {
      this.version = version;
    }
    @JsonProperty("info")
    public void setInfo(Information info) {
      this.info = info;
    }
    @JsonProperty("servers")
    public void setServers(List<Server> servers) {
      this.servers = servers;
    }
    @JsonProperty("paths")
    public void setPaths(Map<String, Path> paths) {
      this.paths = paths;
    }
    @JsonProperty("components")
    public void setComponents(Components components) {
      this.components = components;
    }
    @Override
    public String getVersion() { throw new UnsupportedOperationException(); }
    @Override
    public Information getInfo() { throw new UnsupportedOperationException(); }
    @Override
    public List<Server> getServers() { throw new UnsupportedOperationException(); }
    @Override
    public Map<String, Path> getPaths() { throw new UnsupportedOperationException(); }
    @Override
    public Components getComponents() { throw new UnsupportedOperationException(); }
  }

  /**
   * @param json A JSON-bindable data structure
   * @return An immutable value type
   * @deprecated Do not use this method directly, it exists only for the <em>Jackson</em>-binding infrastructure
   */
  @Deprecated
  @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
  static ImmutableSpecification fromJson(Json json) {
    ImmutableSpecification.Builder builder = ImmutableSpecification.builder();
    if (json.version != null) {
      builder.version(json.version);
    }
    if (json.info != null) {
      builder.info(json.info);
    }
    if (json.servers != null) {
      builder.addAllServers(json.servers);
    }
    if (json.paths != null) {
      builder.putAllPaths(json.paths);
    }
    if (json.components != null) {
      builder.components(json.components);
    }
    return builder.build();
  }

  /**
   * Creates an immutable copy of a {@link Specification} value.
   * Uses accessors to get values to initialize the new immutable instance.
   * If an instance is already immutable, it is returned as is.
   * @param instance The instance to copy
   * @return A copied immutable Specification instance
   */
  public static ImmutableSpecification copyOf(Specification instance) {
    if (instance instanceof ImmutableSpecification) {
      return (ImmutableSpecification) instance;
    }
    return ImmutableSpecification.builder()
        .from(instance)
        .build();
  }

  /**
   * Creates a builder for {@link ImmutableSpecification ImmutableSpecification}.
   * <pre>
   * ImmutableSpecification.builder()
   *    .version(String) // required {@link Specification#getVersion() version}
   *    .info(zeenea.connector.openapi.client.model.Information) // required {@link Specification#getInfo() info}
   *    .addServers|addAllServers(zeenea.connector.openapi.client.model.Server) // {@link Specification#getServers() servers} elements
   *    .putPaths|putAllPaths(String =&gt; zeenea.connector.openapi.client.model.Path) // {@link Specification#getPaths() paths} mappings
   *    .components(zeenea.connector.openapi.client.model.Components) // required {@link Specification#getComponents() components}
   *    .build();
   * </pre>
   * @return A new ImmutableSpecification builder
   */
  public static ImmutableSpecification.Builder builder() {
    return new ImmutableSpecification.Builder();
  }

  /**
   * Builds instances of type {@link ImmutableSpecification ImmutableSpecification}.
   * Initialize attributes and then invoke the {@link #build()} method to create an
   * immutable instance.
   * <p><em>{@code Builder} is not thread-safe and generally should not be stored in a field or collection,
   * but instead used immediately to create instances.</em>
   */
  @Generated(from = "Specification", generator = "Immutables")
  @NotThreadSafe
  public static final class Builder {
    private static final long INIT_BIT_VERSION = 0x1L;
    private static final long INIT_BIT_INFO = 0x2L;
    private static final long INIT_BIT_COMPONENTS = 0x4L;
    private long initBits = 0x7L;

    private @Nullable String version;
    private @Nullable Information info;
    private List<Server> servers = new ArrayList<Server>();
    private Map<String, Path> paths = new LinkedHashMap<String, Path>();
    private @Nullable Components components;

    private Builder() {
    }

    /**
     * Fill a builder with attribute values from the provided {@code Specification} instance.
     * Regular attribute values will be replaced with those from the given instance.
     * Absent optional values will not replace present values.
     * Collection elements and entries will be added, not replaced.
     * @param instance The instance from which to copy values
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder from(Specification instance) {
      Objects.requireNonNull(instance, "instance");
      this.version(instance.getVersion());
      this.info(instance.getInfo());
      addAllServers(instance.getServers());
      putAllPaths(instance.getPaths());
      this.components(instance.getComponents());
      return this;
    }

    /**
     * Initializes the value for the {@link Specification#getVersion() version} attribute.
     * @param version The value for version 
     * @return {@code this} builder for use in a chained invocation
     */
    @JsonProperty("openapi")
    public final Builder version(String version) {
      this.version = Objects.requireNonNull(version, "version");
      initBits &= ~INIT_BIT_VERSION;
      return this;
    }

    /**
     * Initializes the value for the {@link Specification#getInfo() info} attribute.
     * @param info The value for info 
     * @return {@code this} builder for use in a chained invocation
     */
    @JsonProperty("info")
    public final Builder info(Information info) {
      this.info = Objects.requireNonNull(info, "info");
      initBits &= ~INIT_BIT_INFO;
      return this;
    }

    /**
     * Adds one element to {@link Specification#getServers() servers} list.
     * @param element A servers element
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder addServers(Server element) {
      this.servers.add(Objects.requireNonNull(element, "servers element"));
      return this;
    }

    /**
     * Adds elements to {@link Specification#getServers() servers} list.
     * @param elements An array of servers elements
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder addServers(Server... elements) {
      for (Server element : elements) {
        this.servers.add(Objects.requireNonNull(element, "servers element"));
      }
      return this;
    }


    /**
     * Sets or replaces all elements for {@link Specification#getServers() servers} list.
     * @param elements An iterable of servers elements
     * @return {@code this} builder for use in a chained invocation
     */
    @JsonProperty("servers")
    public final Builder servers(Iterable<? extends Server> elements) {
      this.servers.clear();
      return addAllServers(elements);
    }

    /**
     * Adds elements to {@link Specification#getServers() servers} list.
     * @param elements An iterable of servers elements
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder addAllServers(Iterable<? extends Server> elements) {
      for (Server element : elements) {
        this.servers.add(Objects.requireNonNull(element, "servers element"));
      }
      return this;
    }

    /**
     * Put one entry to the {@link Specification#getPaths() paths} map.
     * @param key The key in the paths map
     * @param value The associated value in the paths map
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder putPaths(String key, Path value) {
      this.paths.put(
          Objects.requireNonNull(key, "paths key"),
          Objects.requireNonNull(value, value == null ? "paths value for key: " + key : null));
      return this;
    }

    /**
     * Put one entry to the {@link Specification#getPaths() paths} map. Nulls are not permitted
     * @param entry The key and value entry
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder putPaths(Map.Entry<String, ? extends Path> entry) {
      String k = entry.getKey();
      Path v = entry.getValue();
      this.paths.put(
          Objects.requireNonNull(k, "paths key"),
          Objects.requireNonNull(v, v == null ? "paths value for key: " + k : null));
      return this;
    }

    /**
     * Sets or replaces all mappings from the specified map as entries for the {@link Specification#getPaths() paths} map. Nulls are not permitted
     * @param entries The entries that will be added to the paths map
     * @return {@code this} builder for use in a chained invocation
     */
    @JsonProperty("paths")
    public final Builder paths(Map<String, ? extends Path> entries) {
      this.paths.clear();
      return putAllPaths(entries);
    }

    /**
     * Put all mappings from the specified map as entries to {@link Specification#getPaths() paths} map. Nulls are not permitted
     * @param entries The entries that will be added to the paths map
     * @return {@code this} builder for use in a chained invocation
     */
    public final Builder putAllPaths(Map<String, ? extends Path> entries) {
      for (Map.Entry<String, ? extends Path> e : entries.entrySet()) {
        String k = e.getKey();
        Path v = e.getValue();
        this.paths.put(
            Objects.requireNonNull(k, "paths key"),
            Objects.requireNonNull(v, v == null ? "paths value for key: " + k : null));
      }
      return this;
    }

    /**
     * Initializes the value for the {@link Specification#getComponents() components} attribute.
     * @param components The value for components 
     * @return {@code this} builder for use in a chained invocation
     */
    @JsonProperty("components")
    public final Builder components(Components components) {
      this.components = Objects.requireNonNull(components, "components");
      initBits &= ~INIT_BIT_COMPONENTS;
      return this;
    }

    /**
     * Builds a new {@link ImmutableSpecification ImmutableSpecification}.
     * @return An immutable instance of Specification
     * @throws java.lang.IllegalStateException if any required attributes are missing
     */
    public ImmutableSpecification build() {
      if (initBits != 0) {
        throw new IllegalStateException(formatRequiredAttributesMessage());
      }
      return new ImmutableSpecification(version, info, List.copyOf(servers), Map.copyOf(paths), components);
    }

    private String formatRequiredAttributesMessage() {
      List<String> attributes = new ArrayList<>();
      if ((initBits & INIT_BIT_VERSION) != 0) attributes.add("version");
      if ((initBits & INIT_BIT_INFO) != 0) attributes.add("info");
      if ((initBits & INIT_BIT_COMPONENTS) != 0) attributes.add("components");
      return "Cannot build Specification, some of required attributes are not set " + attributes;
    }
  }
}
