syntax = "proto3";

package laminardb.delta.v1;

// ─── Common Types ────────────────────────────────────────────────────

message NodeId {
  uint64 id = 1;
}

message EpochFence {
  uint32 partition_id = 1;
  uint64 epoch = 2;
  NodeId owner = 3;
}

message ArrowPayload {
  bytes ipc_message = 1;
  string schema_fingerprint = 2;
}

message ErrorDetail {
  uint32 code = 1;
  string message = 2;
  map<string, string> metadata = 3;
}

// ─── Lookup Service ──────────────────────────────────────────────────

service LookupService {
  // Batch lookup: send keys, receive matching rows.
  rpc BatchLookup(BatchLookupRequest) returns (BatchLookupResponse);
  // Streaming query for large result sets.
  rpc Query(QueryRequest) returns (stream QueryResponse);
}

message BatchLookupRequest {
  string table_id = 1;
  repeated bytes keys = 2;
  EpochFence fence = 3;
  repeated string projection = 4;
}

message BatchLookupResponse {
  ArrowPayload results = 1;
  uint32 hits = 2;
  uint32 misses = 3;
  uint64 latency_nanos = 4;
}

message QueryRequest {
  string table_id = 1;
  string predicate_sql = 2;
  EpochFence fence = 3;
  repeated string projection = 4;
  uint32 max_batch_size = 5;
}

message QueryResponse {
  ArrowPayload batch = 1;
  bool is_last = 2;
}

// ─── Aggregate Service ───────────────────────────────────────────────

service AggregateService {
  // Collect partial aggregates from a remote node.
  rpc CollectPartials(CollectPartialsRequest) returns (CollectPartialsResponse);
  // Merge partial results into a final result.
  rpc MergeResult(MergeResultRequest) returns (MergeResultResponse);
}

message CollectPartialsRequest {
  string pipeline_id = 1;
  string aggregate_name = 2;
  bytes group_key = 3;
  EpochFence fence = 4;
}

message PartialAggregate {
  NodeId source_node = 1;
  uint32 partition_id = 2;
  bytes state = 3;
  int64 watermark_ms = 4;
  uint64 epoch = 5;
}

message CollectPartialsResponse {
  repeated PartialAggregate partials = 1;
}

message MergeResultRequest {
  string pipeline_id = 1;
  string aggregate_name = 2;
  repeated PartialAggregate partials = 3;
}

message MergeResultResponse {
  bytes merged_state = 1;
  bool is_complete = 2;
  int64 min_watermark_ms = 3;
}

// ─── Barrier Service ─────────────────────────────────────────────────

service BarrierService {
  // Inject a checkpoint barrier into a remote node's sources.
  rpc InjectBarrier(InjectBarrierRequest) returns (InjectBarrierResponse);
  // Report that a partition has completed its snapshot.
  rpc ReportSnapshot(ReportSnapshotRequest) returns (ReportSnapshotResponse);
}

message InjectBarrierRequest {
  uint64 checkpoint_id = 1;
  uint64 epoch = 2;
  uint64 flags = 3;
  repeated uint32 target_partitions = 4;
  NodeId coordinator = 5;
}

message InjectBarrierResponse {
  bool accepted = 1;
  repeated uint32 rejected_partitions = 2;
  string reject_reason = 3;
}

message ReportSnapshotRequest {
  uint64 checkpoint_id = 1;
  uint32 partition_id = 2;
  NodeId node = 3;
  uint64 epoch = 4;
  string manifest_path = 5;
  uint64 state_size_bytes = 6;
  uint64 snapshot_duration_ms = 7;
}

message ReportSnapshotResponse {
  bool acknowledged = 1;
}

// ─── Partition Service ───────────────────────────────────────────────

service PartitionService {
  // Prepare a partition for transfer (drain, checkpoint).
  rpc PrepareTransfer(PrepareTransferRequest) returns (PrepareTransferResponse);
  // Stream partition state to the new owner.
  rpc ExecuteTransfer(stream TransferChunk) returns (ExecuteTransferResponse);
  // Confirm successful transfer and activate on new owner.
  rpc ConfirmTransfer(ConfirmTransferRequest) returns (ConfirmTransferResponse);
  // Cancel an in-progress transfer.
  rpc CancelTransfer(CancelTransferRequest) returns (CancelTransferResponse);
}

message PrepareTransferRequest {
  uint32 partition_id = 1;
  uint64 current_epoch = 2;
  uint64 new_epoch = 3;
  NodeId new_owner = 4;
}

message PrepareTransferResponse {
  bool ready = 1;
  string checkpoint_path = 2;
  uint64 state_size_bytes = 3;
  uint64 last_committed_epoch = 4;
}

message TransferChunk {
  uint32 partition_id = 1;
  uint64 epoch = 2;
  uint32 chunk_index = 3;
  bytes data = 4;
  bool is_last = 5;
}

message ExecuteTransferResponse {
  bool success = 1;
  uint64 bytes_received = 2;
  uint32 chunks_received = 3;
}

message ConfirmTransferRequest {
  uint32 partition_id = 1;
  uint64 epoch = 2;
  NodeId new_owner = 3;
}

message ConfirmTransferResponse {
  bool confirmed = 1;
  string error_message = 2;
}

message CancelTransferRequest {
  uint32 partition_id = 1;
  uint64 epoch = 2;
  string reason = 3;
}

message CancelTransferResponse {
  bool cancelled = 1;
}
