syntax = "proto3";
package localdb_sync.v1;
option csharp_namespace = "ZB.MOM.WW.LocalDb.Contracts";

// One long-lived bidirectional stream carries BOTH directions of replication.
service LocalDbSync {
  rpc Sync (stream SyncMessage) returns (stream SyncMessage);
}

message SyncMessage {
  oneof msg {
    Handshake handshake = 1;          // first message each direction
    HandshakeAck handshake_ack = 2;
    DeltaBatch delta_batch = 3;
    DeltaAck delta_ack = 4;
    SnapshotBegin snapshot_begin = 5; // sender switches to snapshot mode
    SnapshotBatch snapshot_batch = 6;
    SnapshotComplete snapshot_complete = 7;
  }
}

message Handshake {
  string node_id = 1;
  uint32 lib_schema_version = 2;
  repeated TableDigest tables = 3;    // fail-closed on any mismatch
  int64 last_applied_remote_seq = 4;  // highest of YOUR oplog seqs I have applied
}
message TableDigest { string table_name = 1; string digest = 2; }
message HandshakeAck { string node_id = 1; bool snapshot_required = 2; }

message OplogEntry {
  int64 seq = 1;
  string table_name = 2;
  string pk_json = 3;
  optional string row_json = 4;       // absent => tombstone
  int64 hlc = 5;                      // UTC-based HLC (physical ms << 16 | counter)
  string node_id = 6;
  bool is_tombstone = 7;
}
message DeltaBatch { repeated OplogEntry entries = 1; }
message DeltaAck { int64 applied_thru_seq = 1; }

message SnapshotBegin { int64 as_of_seq = 1; }
message SnapshotBatch { string table_name = 1; repeated SnapshotRow rows = 2; }
message SnapshotRow {
  string pk_json = 1;
  optional string row_json = 2;
  int64 hlc = 3;
  string node_id = 4;
  bool is_tombstone = 5;
}
message SnapshotComplete { int64 as_of_seq = 1; }
