// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package google.registry.model.reporting;

import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;

import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.time.DateTime;

/** A record of an EPP command that mutated a resource. */
@ReportedOn
@Entity
public class HistoryEntry extends ImmutableObject implements Buildable {

  /** Represents the type of history entry. */
  public enum Type {
    CONTACT_CREATE,
    CONTACT_DELETE,
    CONTACT_DELETE_FAILURE,
    CONTACT_PENDING_DELETE,
    CONTACT_TRANSFER_APPROVE,
    CONTACT_TRANSFER_CANCEL,
    CONTACT_TRANSFER_REJECT,
    CONTACT_TRANSFER_REQUEST,
    CONTACT_UPDATE,
    /**
     * Used for history entries that were allocated as a result of a domain application.
     *
     * <p>Domain applications (and thus allocating from an application) no longer exist, but we have
     * existing domains in the system that were created via allocation and thus have history entries
     * of this type under them, so this is retained for legacy purposes.
     */
    @Deprecated
    DOMAIN_ALLOCATE,
    /**
     * Used for domain registration autorenews explicitly logged by
     * {@link google.registry.batch.ExpandRecurringBillingEventsAction}.
     */
    DOMAIN_AUTORENEW,
    DOMAIN_CREATE,
    DOMAIN_DELETE,
    DOMAIN_RENEW,
    DOMAIN_RESTORE,
    DOMAIN_TRANSFER_APPROVE,
    DOMAIN_TRANSFER_CANCEL,
    DOMAIN_TRANSFER_REJECT,
    DOMAIN_TRANSFER_REQUEST,
    DOMAIN_UPDATE,
    HOST_CREATE,
    HOST_DELETE,
    HOST_DELETE_FAILURE,
    HOST_PENDING_DELETE,
    HOST_UPDATE,
    /** Resource was created by an escrow file import. */
    RDE_IMPORT,
    /**
     * A synthetic history entry created by a tool or back-end migration script outside of the scope
     * of usual EPP flows. These are sometimes needed to serve as parents for billing events or poll
     * messages that otherwise wouldn't have a suitable parent.
     */
    SYNTHETIC
  }

  /** The autogenerated id of this event. */
  @Id
  long id;

  /** The resource this event mutated. */
  @Parent
  Key<? extends EppResource> parent;

  /** The type of history entry. */
  Type type;

  /**
   * The length of time that a create, allocate, renewal, or transfer request was issued for. Will
   * be null for all other types.
   */
  @IgnoreSave(IfNull.class)
  Period period;

  /** The actual EPP xml of the command, stored as bytes to be agnostic of encoding. */
  byte[] xmlBytes;

  /** The time the command occurred, represented by the ofy transaction time.*/
  @Index
  DateTime modificationTime;

  /** The id of the registrar that sent the command. */
  @Index
  String clientId;

  /**
   * For transfers, the id of the other registrar.
   *
   * <p>For requests and cancels, the other registrar is the losing party (because the registrar
   * sending the EPP transfer command is the gaining party). For approves and rejects, the other
   * registrar is the gaining party.
   */
  String otherClientId;

  /** Transaction id that made this change, or null if the entry was not created by a flow. */
  @Nullable Trid trid;

  /** Whether this change was created by a superuser. */
  boolean bySuperuser;

  /** Reason for the change. */
  String reason;

  /** Whether this change was requested by a registrar. */
  Boolean requestedByRegistrar;

  /**
   * Logging field for transaction reporting.
   *
   * <p>This will be empty for any HistoryEntry generated before this field was added. This will
   * also be empty if the HistoryEntry refers to an EPP mutation that does not affect domain
   * transaction counts (such as contact or host mutations).
   */
  Set<DomainTransactionRecord> domainTransactionRecords;

  public Key<? extends EppResource> getParent() {
    return parent;
  }

  public Type getType() {
    return type;
  }

  public Period getPeriod() {
    return period;
  }

  public byte[] getXmlBytes() {
    return xmlBytes;
  }

  public DateTime getModificationTime() {
    return modificationTime;
  }

  public String getClientId() {
    return clientId;
  }

  public String getOtherClientId() {
    return otherClientId;
  }

  /** Returns the TRID, which may be null if the entry was not created by a normal flow. */
  @Nullable public Trid getTrid() {
    return trid;
  }

  public boolean getBySuperuser() {
    return bySuperuser;
  }

  public String getReason() {
    return reason;
  }

  public Boolean getRequestedByRegistrar() {
    return requestedByRegistrar;
  }

  public ImmutableSet<DomainTransactionRecord> getDomainTransactionRecords() {
    return nullToEmptyImmutableCopy(domainTransactionRecords);
  }

  @Override
  public Builder asBuilder() {
    return new Builder(clone(this));
  }

  /** A builder for {@link HistoryEntry} since it is immutable */
  public static class Builder extends Buildable.Builder<HistoryEntry> {
    public Builder() {}

    public Builder(HistoryEntry instance) {
      super(instance);
    }

    public Builder setParent(EppResource parent) {
      getInstance().parent = Key.create(parent);
      return this;
    }

    public Builder setParent(Key<? extends EppResource> parentKey) {
      getInstance().parent = parentKey;
      return this;
    }

    public Builder setType(Type type) {
      getInstance().type = type;
      return this;
    }

    public Builder setPeriod(Period period) {
      getInstance().period = period;
      return this;
    }

    public Builder setXmlBytes(byte[] xmlBytes) {
      getInstance().xmlBytes = xmlBytes;
      return this;
    }

    public Builder setModificationTime(DateTime modificationTime) {
      getInstance().modificationTime = modificationTime;
      return this;
    }

    public Builder setClientId(String clientId) {
      getInstance().clientId = clientId;
      return this;
    }

    public Builder setOtherClientId(String otherClientId) {
      getInstance().otherClientId = otherClientId;
      return this;
    }

    public Builder setTrid(Trid trid) {
      getInstance().trid = trid;
      return this;
    }

    public Builder setBySuperuser(boolean bySuperuser) {
      getInstance().bySuperuser = bySuperuser;
      return this;
    }

    public Builder setReason(String reason) {
      getInstance().reason = reason;
      return this;
    }

    public Builder setRequestedByRegistrar(Boolean requestedByRegistrar) {
      getInstance().requestedByRegistrar = requestedByRegistrar;
      return this;
    }

    public Builder setDomainTransactionRecords(
        ImmutableSet<DomainTransactionRecord> domainTransactionRecords) {
      getInstance().domainTransactionRecords = domainTransactionRecords;
      return this;
    }
  }
}
