syntax = "proto3";

package nexuscrm.runtime.v1;

import "common/v1/common.proto";

option go_package = "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1;runtimev1";

service RecordsService {
  rpc ReadSelectedRoot(ReadSelectedRootRequest) returns (ReadSelectedRootResponse);
  rpc EvaluateRecordAccess(EvaluateRecordAccessRequest) returns (EvaluateRecordAccessResponse);
}

// Identity and authority are authenticated transport context. They are never
// caller-selected semantic request fields.
message ReadSelectedRootRequest {
  string object_id = 1;
  string record_id = 2;
  repeated string selected_root_fields = 3;
  optional uint64 min_catalog_revision = 4;
}

enum ValueKind {
  VALUE_KIND_UNSPECIFIED = 0;
  VALUE_KIND_STRING = 1;
  VALUE_KIND_BOOLEAN = 2;
  VALUE_KIND_INTEGER = 3;
  VALUE_KIND_DECIMAL = 4;
  VALUE_KIND_DATE = 5;
  VALUE_KIND_DATETIME = 6;
  VALUE_KIND_REFERENCE = 7;
}

message DecimalValue {
  string coefficient = 1;
  uint32 scale = 2;
}

message DateValue {
  string calendar_date = 1;
}

message DateTimeValue {
  string utc_rfc3339 = 1;
}

message ReferenceValue {
  string identifier = 1;
}

message NullValue {}

// Exactly one value arm is present. Null is explicit; an absent or FLS-hidden
// field has no SelectedRootField entry.
message SelectedRootField {
  string name = 1;
  ValueKind declared_kind = 2;
  oneof value {
    string string_value = 3;
    bool boolean_value = 4;
    sint64 integer_value = 5;
    DecimalValue decimal_value = 6;
    DateValue date_value = 7;
    DateTimeValue datetime_value = 8;
    ReferenceValue reference_value = 9;
    NullValue null_value = 10;
  }
}

message ReadSelectedRootResponse {
  string record_id = 1;
  // Canonical field API-name order, ascending by bytewise UTF-8 comparison.
  repeated SelectedRootField fields = 2;
  nexuscrm.common.v1.ConsistencyReceipts receipts = 3;
}

// Identity and authority are authenticated transport context. They are never
// caller-selected semantic request fields. An absent object_id asks Runtime to
// resolve every record identifier internally as part of this one coarse RPC.
message EvaluateRecordAccessRequest {
  optional string object_id = 1;
  repeated string record_ids = 2;
}

// A result exists only for a record the caller may read. Missing records,
// read-denied records, unknown object resolution, and forbidden resolved object
// groups are all represented by absence, never by read=false.
message RecordAccessResult {
  string record_id = 1;
  bool read = 2;
  bool update = 3;
  bool delete = 4;
  bool locked = 5;
}

message EvaluateRecordAccessResponse {
  // Unique canonical results, ascending by bytewise UTF-8 record identifier.
  repeated RecordAccessResult results = 1;
  nexuscrm.common.v1.ConsistencyReceipts receipts = 2;
}
