namespace NATS.Server.Raft;

/// <summary>
/// Tracks replication and health state for a single RAFT peer.
/// Go reference: raft.go peer tracking fields (nextIndex, matchIndex, last contact).
/// </summary>
public sealed class RaftPeerState
{
    /// <summary>
    /// The peer's unique node identifier.
    /// </summary>
    public required string PeerId { get; init; }

    /// <summary>
    /// The next log index to send to this peer (leader use only).
    /// </summary>
    public long NextIndex { get; set; } = 1;

    /// <summary>
    /// The highest log index known to be replicated on this peer.
    /// </summary>
    public long MatchIndex { get; set; }

    /// <summary>
    /// Timestamp of the last successful communication with this peer.
    /// </summary>
    public DateTime LastContact { get; set; } = DateTime.UtcNow;

    /// <summary>
    /// Whether this peer is considered active in the cluster.
    /// </summary>
    public bool Active { get; set; } = true;

    /// <summary>
    /// Distance between next index and acknowledged match index.
    /// Go reference: raft.go Peer.Lag.
    /// </summary>
    public long Lag { get; set; }

    /// <summary>
    /// Cached "current" flag based on last contact freshness checks.
    /// Go reference: raft.go Peer.Current.
    /// </summary>
    public bool Current { get; set; }

    /// <summary>
    /// Recomputes lag from current next/match indices.
    /// </summary>
    public void RecalculateLag()
        => Lag = Math.Max(0, NextIndex - (MatchIndex + 1));

    /// <summary>
    /// Refreshes the cached Current flag using the provided freshness window.
    /// </summary>
    public void RefreshCurrent(TimeSpan window)
        => Current = DateTime.UtcNow - LastContact < window;

    /// <summary>
    /// Returns true if this peer has been contacted within the election timeout window.
    /// Go reference: raft.go isCurrent check.
    /// </summary>
    public bool IsCurrent(TimeSpan electionTimeout)
    {
        RefreshCurrent(electionTimeout);
        return Current;
    }

    /// <summary>
    /// Returns true if this peer is both active and has been contacted within the health threshold.
    /// </summary>
    public bool IsHealthy(TimeSpan healthThreshold)
    {
        RefreshCurrent(healthThreshold);
        return Active && Current;
    }
}
