/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.ratis.server.leader;

import org.apache.ratis.protocol.RaftPeer;
import org.apache.ratis.protocol.RaftPeerId;
import org.apache.ratis.util.Timestamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.function.LongUnaryOperator;

/**
 * Information of a follower, provided the local server is the Leader
 */
public interface FollowerInfo {
  Logger LOG = LoggerFactory.getLogger(FollowerInfo.class);

  /** @return the name of this object. */
  String getName();

  /** @return this follower's peer id. */
  RaftPeerId getId();

  /**
   * Return this follower's {@link RaftPeer}.
   * To obtain the {@link RaftPeerId}, use {@link #getId()} which is more efficient than this method.
   *
   * @return this follower's peer info.
   */
  RaftPeer getPeer();

  /** @return the matchIndex acknowledged by this follower. */
  long getMatchIndex();

  /** Update this follower's matchIndex. */
  boolean updateMatchIndex(long newMatchIndex);

  /** @return the commitIndex acknowledged by this follower. */
  long getCommitIndex();

  /** Update follower's commitIndex. */
  boolean updateCommitIndex(long newCommitIndex);

  /** @return the snapshotIndex acknowledged by this follower. */
  long getSnapshotIndex();

  /** Set follower's snapshotIndex. */
  void setSnapshotIndex(long newSnapshotIndex);

  /** Acknowledge that Follower attempted to install a snapshot. It does not guarantee that the installation was
   * successful. This helps to determine whether Follower can come out of bootstrap process. */
  void setAttemptedToInstallSnapshot();

  /** Return true if install snapshot has been attempted by the Follower at least once. Used to verify if
   * Follower tried to install snapshot during bootstrap process. */
  boolean hasAttemptedToInstallSnapshot();

  /** @return the nextIndex for this follower. */
  long getNextIndex();

  /** Increase the nextIndex for this follower. */
  void increaseNextIndex(long newNextIndex);

  /** Decrease the nextIndex for this follower. */
  void decreaseNextIndex(long newNextIndex);

  /** Set the nextIndex for this follower. */
  void setNextIndex(long newNextIndex);

  /** Update the nextIndex for this follower. */
  void updateNextIndex(long newNextIndex);

  /** Set the nextIndex for this follower. */
  void computeNextIndex(LongUnaryOperator op);

  /** @return the lastRpcResponseTime . */
  Timestamp getLastRpcResponseTime();

  /** @return the lastRpcSendTime . */
  Timestamp getLastRpcSendTime();

  /** Update lastRpcResponseTime to the current time. */
  void updateLastRpcResponseTime();

  /** Update lastRpcSendTime to the current time. */
  void updateLastRpcSendTime(boolean isHeartbeat);

  /** @return the latest of the lastRpcSendTime and the lastRpcResponseTime . */
  Timestamp getLastRpcTime();

  /** @return the latest heartbeat send time. */
  Timestamp getLastHeartbeatSendTime();

  /** @return the send time of last responded rpc */
  Timestamp getLastRespondedAppendEntriesSendTime();

  /** Update lastRpcResponseTime and LastRespondedAppendEntriesSendTime */
  void updateLastRespondedAppendEntriesSendTime(Timestamp sendTime);

  /** @return the error state. */
  ErrorState getErrorState();

  /** Error state such as the count for consecutive errors. */
  interface ErrorState {
    /**
     * If it is an error, increment the count; otherwise, reset the count to 0.
     *
     * @return the updated error count.
     */
    int updateErrorCount(boolean isError);

    /**
     * Each error count is returned only once.
     * For the subsequent calls of the same error count, it returns 0.
     * <p>
     * For example,
     * 1. Error count is 3
     * 2. Calling getErrorCountToDelay() returns 3
     * 3. Calling getErrorCountToDelay() again returns 0
     * 4. Calling updateErrorCount(true) to increment error count to 4
     * 5. Calling getErrorCountToDelay() returns 4
     * 6. Calling getErrorCountToDelay() again returns 0
     * 7. Calling updateErrorCount(false) resets error count to 0
     *
     * @return each error count only once.
     */
    int getErrorCountToDelay();
  }
}
