syntax = "proto3";
package geode;

option go_package = "geodedb.com/geode/proto";

// GeodeService provides gRPC access to the Geode database.
service GeodeService {
  rpc Handshake(HelloRequest) returns (HelloResponse);
  rpc Execute(ExecuteRequest) returns (stream ExecutionResponse);
  rpc Ping(PingRequest) returns (PingResponse);
  rpc Begin(BeginRequest) returns (BeginResponse);
  rpc Commit(CommitRequest) returns (CommitResponse);
  rpc Rollback(RollbackRequest) returns (RollbackResponse);
  rpc GetCdcDiagnostics(CdcDiagnosticsRequest) returns (CdcDiagnosticsResponse);
  rpc ControlCdc(CdcControlRequest) returns (CdcControlResponse);
}

// ============================================================================
// Transport Wrappers (QUIC framing)
// ============================================================================
message ClientPacket {
  oneof msg {
    QuicClientMessage quic = 1;
  }
}

message ServerPacket {
  oneof msg {
    QuicServerMessage quic = 1;
  }
}

// ============================================================================
// QUIC Messages
// ============================================================================
message QuicClientMessage {
  oneof msg {
    HelloRequest hello = 1;
    ExecuteRequest execute = 2;
    PullRequest pull = 3;
    PingRequest ping = 4;
    CdcDiagnosticsRequest cdc_diag = 5;
    CdcControlRequest cdc_ctrl = 6;
    BeginRequest begin = 7;
    CommitRequest commit = 8;
    RollbackRequest rollback = 9;
    SavepointRequest savepoint = 10;
    RollbackToRequest rollback_to = 11;
    BackupRequest backup = 12;
    RestoreRequest restore = 13;
    UploadBackupRequest upload_backup = 14;
  }
}

message QuicServerMessage {
  oneof msg {
    HelloResponse hello = 1;
    ExecutionResponse execute = 2;
    PullResponse pull = 3;
    PingResponse ping = 4;
    CdcDiagnosticsResponse cdc_diag = 5;
    CdcControlResponse cdc_ctrl = 6;
    BeginResponse begin = 7;
    CommitResponse commit = 8;
    RollbackResponse rollback = 9;
    SavepointResponse savepoint = 10;
    RollbackToResponse rollback_to = 11;
    BackupResponse backup = 12;
    RestoreResponse restore = 13;
    UploadBackupResponse upload_backup = 14;
  }
}

// ============================================================================
// Authentication (HELLO)
// ============================================================================
message HelloRequest {
  string username = 1;
  string password = 2;
  optional string tenant_id = 3;
  string client_name = 4;
  string client_version = 5;
  string wanted_conformance = 6;
  optional string graph = 7;
  optional string role = 8;       // FLE role for field-level access control
}

message HelloResponse {
  bool success = 1;
  string session_id = 2;
  string error_message = 3;
  repeated string capabilities = 4;
  bool password_reset_required = 5;
  optional string graph = 6;
}

// ============================================================================
// Query Execution (RUN_GQL + PULL)
// ============================================================================
message ExecuteRequest {
  string session_id = 1;
  string query = 2;
  repeated Param params = 3;
}

message Param {
  string name = 1;
  Value value = 2;
}

message PullRequest {
  uint64 request_id = 1;
  uint32 page_size = 2;
  string session_id = 3; // Required for gRPC; ignored for QUIC
}

message PullResponse {
  ExecutionResponse response = 1;
}

// ============================================================================
// Execution Responses
// ============================================================================
message Status {
  string status_class = 1; // e.g., "00000"
  string status_subclass = 2;
  repeated string additional_statuses = 3;
  repeated string flagger_findings = 4;
}

message ExecutionResponse {
  Status status = 1;
  oneof payload {
    SchemaDefinition schema = 2;
    DataPage page = 3;
    Error error = 4;
    ExecutionMetrics metrics = 5;
    ExplainPayload explain = 6;
    ProfilePayload profile = 7;
    Heartbeat heartbeat = 8;
  }
}

message SchemaDefinition {
  repeated ColumnDefinition columns = 1;
}

message ColumnDefinition {
  string name = 1;
  string type = 2;
}

message DataPage {
  repeated Row rows = 1;
  bool final = 2;
  bool ordered = 3;
  repeated string order_keys = 4;
}

message Row {
  repeated Value values = 1;
}

// ----------------------------------------------------------------------------
// Value Encoding
// ----------------------------------------------------------------------------
message Value {
  oneof kind {
    NullValue null_val = 1;
    IntValue int_val = 2;
    DoubleValue double_val = 3;
    bool bool_val = 4;
    StringValue string_val = 5;
    DecimalValue decimal_val = 6;
    BytesValue bytes_val = 7;
    ListValue list_val = 8;
    MapValue map_val = 9;
    NodeValue node_val = 10;
    EdgeValue edge_val = 11;
    PathValue path_val = 12;
    ExtendedValue ext_val = 13;
  }
}

message NullValue {}

enum IntKind {
  INT_KIND_UNSPECIFIED = 0;
  INT = 1;
  SMALLINT = 2;
  BIGINT = 3;
}

message IntValue {
  int64 value = 1;
  IntKind kind = 2;
}

enum FloatKind {
  FLOAT_KIND_UNSPECIFIED = 0;
  DOUBLE = 1;
  REAL = 2;
}

message DoubleValue {
  double value = 1;
  FloatKind kind = 2;
}

enum StringKind {
  STRING_KIND_UNSPECIFIED = 0;
  STRING = 1;
  CHAR = 2;
  VARCHAR = 3;
  TEXT = 4;
}

message StringValue {
  string value = 1;
  StringKind kind = 2;
}

message DecimalValue {
  string coeff = 1; // i128 as decimal string
  uint32 scale = 2;
  uint32 orig_scale = 3;
  string orig_repr = 4;
}

enum BytesKind {
  BYTES_KIND_UNSPECIFIED = 0;
  BYTEA = 1;
  RAW = 2;
}

message BytesValue {
  bytes value = 1;
  BytesKind kind = 2;
}

message ListValue {
  repeated Value values = 1;
}

message MapEntry {
  string key = 1;
  Value value = 2;
}

message MapValue {
  repeated MapEntry entries = 1;
}

message NodeValue {
  uint64 id = 1;
  repeated string labels = 2;
  repeated MapEntry properties = 3;
}

message EdgeValue {
  uint64 id = 1;
  uint64 from_id = 2;
  uint64 to_id = 3;
  string label = 4;
  repeated MapEntry properties = 5;
}

message PathValue {
  repeated NodeValue nodes = 1;
  repeated EdgeValue edges = 2;
}

message ExtendedValue {
  string type_name = 1;
  oneof data {
    string text = 2;
    bytes bytes = 3;
    int64 int_val = 4;
    double double_val = 5;
    bool bool_val = 6;
  }
}

message Error {
  string code = 1;
  string message = 2;
  string type = 3; // "ERROR"
  string anchor = 4;
}

message ExecutionMetrics {
  int64 parse_duration_ns = 1;
  int64 plan_duration_ns = 2;
  int64 execute_duration_ns = 3;
  int64 total_duration_ns = 4;
}

message ExplainOp {
  uint32 idx = 1;
  string kind = 2;
  int64 est_rows = 3;
  int64 cost = 4;
}

message ExplainTotals {
  int64 est_rows = 1;
  int64 cost = 2;
}

message ExplainProperties {
  bool ordered = 1;
  uint64 limit = 2;
  uint64 offset = 3;
  bool distinct = 4;
  string union_mode = 5;
  uint32 union_part_count = 6;
}

message ExplainCalibrationEntry {
  uint32 idx = 1;
  double est_rows = 2;
  double actual_rows = 3;
}

message ExplainCalibration {
  double mape = 1;
  repeated ExplainCalibrationEntry entries = 2;
}

message ExplainPayload {
  string schema = 1;
  repeated ExplainOp ops = 2;
  ExplainTotals totals = 3;
  ExplainProperties properties = 4;
  ExplainCalibration calibration = 5;
  uint32 profile_version = 6;
}

message ProfileOp {
  string op = 1;
  string phase = 2;
  uint32 id = 3;
  uint32 op_index = 4;
  uint64 input_rows = 5;
  uint64 rows = 6;
  uint64 time_ns = 7;
  uint64 cpu_time_ns = 8;
  uint64 bytes_out = 9;
  uint64 bytes_alloc = 10;
  uint64 estimate_bytes = 11;
  uint64 estimate_vs_actual = 12;
  uint64 percent_peak = 13;
  uint64 percent_net = 14;
  uint64 cumulative_time_ns = 15;
  uint64 freed_bytes = 16;
  uint64 net_after_op = 17;
  uint64 error_bytes = 18;
  uint64 error_abs_pct = 19;
  string index_name = 20;
  double selectivity_est = 21;
}

message ProfilePeakContributor {
  string op = 1;
  uint64 bytes_alloc = 2;
}

message ProfileTotals {
  uint64 time_ns = 1;
  uint64 peak_bytes = 2;
}

message ProfileSpills {
  uint64 sort_spills = 1;
  uint64 distinct_spills = 2;
  uint64 union_spills = 3;
  string spill_reason = 4;
  uint64 peak_bytes_at_spill = 5;
  uint32 first_spill_op_index = 6;
}

message ProfileMemory {
  uint64 net_bytes = 1;
  uint64 peak_bytes = 2;
  uint64 total_alloc_bytes = 3;
}

message ProfilePlannerEstimates {
  uint64 sum_estimate_bytes = 1;
  uint64 sum_actual_bytes = 2;
  uint32 count_estimated_ops = 3;
  double min_error_abs_pct = 4;
  double max_error_abs_pct = 5;
  double mean_error_abs_pct = 6;
  double median_error_abs_pct = 7;
  double stddev_error_abs_pct = 8;
  double adjusted_estimate_factor = 9;
}

message ProfileMemCurvePoint {
  uint32 op_index = 1;
  uint64 net_after_op = 2;
}

message ProfileSetOp {
  string type = 1;
  string mode = 2;
  uint64 input_rows = 3;
  uint64 output_rows = 4;
  uint64 hash_dedup_size = 5;
  uint64 distinct_fill_pct = 6;
}

message ProfilePayload {
  uint32 profile_version = 1;
  repeated ProfileOp ops = 2;
  repeated ProfilePeakContributor peak_contributors = 3;
  ProfileTotals totals = 4;
  uint64 total_time_ns = 5;
  ProfileSpills spills = 6;
  ProfileMemory memory = 7;
  ProfilePlannerEstimates planner_estimates = 8;
  repeated ProfileMemCurvePoint mem_curve = 9;
  ProfileSetOp setop = 10;
  uint64 hashagg_spills = 11;
  string hashagg_spill_reason = 12;
  uint64 committed_txns = 13;
  uint64 graph_store_nodes = 14;
  uint64 graph_store_edges = 15;
  bool graph_store_dirty = 16;
  repeated string flagger_findings = 17;
  bool compact = 18;
}

message Heartbeat {}

// ============================================================================
// Utilities (PING)
// ============================================================================
message PingRequest {}
message PingResponse {
  bool ok = 1;
}

// ============================================================================
// Transactions
// ============================================================================
message BeginRequest {
  bool read_only = 1;
  string session_id = 2; // Required for gRPC; ignored for QUIC
}
message BeginResponse {
  string session_id = 1;
  string tx_id = 2;
}
message CommitRequest {
  string session_id = 1; // Required for gRPC; ignored for QUIC
}
message CommitResponse {
  bool success = 1;
}
message RollbackRequest {
  string session_id = 1; // Required for gRPC; ignored for QUIC
}
message RollbackResponse {
  bool success = 1;
}
message SavepointRequest {
  string name = 1;
  string session_id = 2; // Required for gRPC; ignored for QUIC
}
message SavepointResponse {
  bool success = 1;
}
message RollbackToRequest {
  string name = 1;
  string session_id = 2; // Required for gRPC; ignored for QUIC
}
message RollbackToResponse {
  bool success = 1;
}

// ============================================================================
// CDC
// ============================================================================
message CdcDiagnosticsRequest {}
message CdcDiagnosticsConfig {
  bool enabled = 1;
  uint64 malformed_snapshot_interval = 2;
  uint32 malformed_hash_retain = 3;
  double malformed_warn_pct = 4;
  double malformed_abort_pct = 5;
  uint32 flush_interval_ms = 6;
  uint32 batch_size = 7;
  uint64 pending_backpressure_watermark = 8;
}

message CdcDiagnosticsEngine {
  bool is_running = 1;
  int64 last_flush_ms = 2;
  uint64 peak_buffer = 3;
  uint64 current_buffer = 4;
  uint32 dynamic_batch_size = 5;
  uint32 batch_adjustments = 6;
}

message CdcMalformedSnapshot {
  uint64 count = 1;
  int64 ts_ms = 2;
}

message CdcMalformedHash {
  uint64 count = 1;
  uint64 hash = 2;
  int64 ts_ms = 3;
  string prefix_hex = 4;
}

message CdcDiagnosticsResponse {
  uint64 malformed_change_records = 1;
  uint64 total_change_attempts = 2;
  double malformed_ratio = 3;
  bool guardrail_warn_triggered = 4;
  bool guardrail_abort_triggered = 5;
  int64 first_warn_ts_ms = 6;
  int64 first_abort_ts_ms = 7;
  uint64 guardrail_epoch = 8;
  int64 uptime_ms = 9;
  bool backpressure = 10;
  CdcDiagnosticsConfig config = 11;
  CdcDiagnosticsEngine engine = 12;
  repeated CdcMalformedSnapshot malformed_snapshots = 13;
  repeated CdcMalformedHash malformed_hashes = 14;
  string version = 15;
  int64 timestamp_ms = 16;
}
message CdcControlRequest {
  string action = 1;
}
message CdcControlResponse {
  bool success = 1;
  string status = 2;
  uint64 guardrail_epoch = 3;
}

// ============================================================================
// Backup / Restore
// ============================================================================
message BackupRequest {
  string backup_type = 1; // "full"
  string session_id = 2;
  bool compress = 3;
}
message BackupResponse {
  bool success = 1;
  string message = 2;
  string backup_id = 3;
  string backup_type = 4;
  uint64 size_bytes = 5;
  string compression = 6;
  string checksum = 7;
}
message RestoreRequest {
  string target_time = 1;
  bool confirm = 2;
  string session_id = 3;
}
message RestoreResponse {
  bool success = 1;
  string message = 2;
  string restore_dir = 3;
  string target_time = 4;
  uint64 restore_timestamp = 5;
}
message UploadBackupRequest {
  uint64 size_bytes = 1;
  string checksum = 2;
  string session_id = 3;
  // GAP-0880: full backup artifact bytes (GEOB header + payload). Required;
  // metadata-only uploads are rejected by the server.
  bytes payload = 4;
}
message UploadBackupResponse {
  bool success = 1;
  string message = 2;
  string upload_path = 3;
}
