using Google.Protobuf;
using Google.Protobuf.Collections;
using Grpc.Core;
namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc;
/// Bounds audit batches using complete protobuf row sizes, length/tag prefixes, and gRPC framing.
public static class AuditWireBudget
{
/// Five-byte gRPC frame plus two bytes reserved for a true MoreAvailable field.
public const int InitialBytes = 7;
///
/// Appends one row to a repeated-message collection if it fits. A row larger than the
/// normal budget travels alone, up to the hard channel ceiling. Oversize rows are retained
/// locally and fail explicitly with their identity; they are never acknowledged or truncated.
///
/// The protobuf message type of the rows.
/// The repeated field to append to.
/// The row to append.
/// The running wire size of the batch; increased by the row's size when appended.
/// Identity of the row, used in the error raised when it exceeds the hard ceiling.
/// true when the row was appended; false when it does not fit in the current batch.
public static bool TryAppend(RepeatedField rows, T row, ref int wireBytes, string rowIdentity)
where T : IMessage
{
var rowBytes = 1L + CodedOutputStream.ComputeMessageSize(row);
if (rows.Count > 0 && wireBytes + rowBytes > GrpcMessageLimits.AuditBatchBytes) return false;
if (rowBytes + InitialBytes > GrpcMessageLimits.MaxMessageBytes)
throw new RpcException(new Status(StatusCode.ResourceExhausted,
$"Audit row {rowIdentity} requires {rowBytes + InitialBytes} wire bytes, exceeding {GrpcMessageLimits.MaxMessageBytes}; retained locally. Reduce its payload or raise coordinated peer limits before retrying."));
rows.Add(row);
wireBytes += (int)rowBytes;
return true;
}
}