package io.sentry.protocol;

import io.sentry.ILogger;
import io.sentry.JsonDeserializer;
import io.sentry.JsonSerializable;
import io.sentry.ObjectReader;
import io.sentry.ObjectWriter;
import io.sentry.SentryUUID;
import io.sentry.util.StringUtils;
import io.sentry.util.UUIDStringUtils;
import java.io.IOException;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class SentryId implements JsonSerializable {

  public static final SentryId EMPTY_ID =
      new SentryId(StringUtils.PROPER_NIL_UUID.replace("-", ""));

  private volatile @Nullable String value;
  private final @Nullable UUID uuid;

  public SentryId() {
    this((UUID) null);
  }

  public SentryId(@Nullable UUID uuid) {
    this.uuid = uuid;
  }

  public SentryId(final @NotNull String sentryIdString) {
    final @NotNull String normalized = StringUtils.normalizeUUID(sentryIdString);
    if (normalized.length() != 32 && normalized.length() != 36) {
      throw new IllegalArgumentException(
          "String representation of SentryId has either 32 (UUID no dashes) "
              + "or 36 characters long (completed UUID). Received: "
              + sentryIdString);
    }
    this.uuid = null;
    this.value = normalized.length() == 36 ? normalized.replace("-", "") : normalized;
  }

  private @NotNull String getValue() {
    String result = value;
    if (result == null) {
      synchronized (this) {
        result = value;
        if (result == null) {
          result =
              uuid != null
                  ? normalize(UUIDStringUtils.toSentryIdString(uuid))
                  : SentryUUID.generateSentryId();
          value = result;
        }
      }
    }
    return result;
  }

  @Override
  public String toString() {
    return getValue();
  }

  @Override
  public boolean equals(final @Nullable Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    SentryId sentryId = (SentryId) o;
    return getValue().equals(sentryId.getValue());
  }

  @Override
  public int hashCode() {
    return getValue().hashCode();
  }

  private @NotNull String normalize(@NotNull String uuidString) {
    return StringUtils.normalizeUUID(uuidString).replace("-", "");
  }

  // JsonSerializable

  @Override
  public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger)
      throws IOException {
    writer.value(toString());
  }

  // JsonElementDeserializer

  public static final class Deserializer implements JsonDeserializer<SentryId> {
    @Override
    public @NotNull SentryId deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger)
        throws Exception {
      return new SentryId(reader.nextString());
    }
  }
}
