// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

syntax = "proto3";

package banyandb.database.v1;

import "banyandb/common/v1/common.proto";
import "banyandb/database/v1/database.proto";
import "banyandb/database/v1/schema.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";

option go_package = "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1";
option java_package = "org.apache.skywalking.banyandb.database.v1";
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {base_path: "/api"};

message StreamRegistryServiceCreateRequest {
  banyandb.database.v1.Stream stream = 1;
}

message StreamRegistryServiceCreateResponse {
  int64 mod_revision = 1;
}

message StreamRegistryServiceUpdateRequest {
  banyandb.database.v1.Stream stream = 1;
}

message StreamRegistryServiceUpdateResponse {
  int64 mod_revision = 1;
}

message StreamRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message StreamRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message StreamRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message StreamRegistryServiceGetResponse {
  banyandb.database.v1.Stream stream = 1;
}

message StreamRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message StreamRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_stream = 2;
}

message StreamRegistryServiceListRequest {
  string group = 1;
}

message StreamRegistryServiceListResponse {
  repeated banyandb.database.v1.Stream stream = 1;
}

service StreamRegistryService {
  rpc Create(StreamRegistryServiceCreateRequest) returns (StreamRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/stream/schema"
      body: "*"
    };
  }

  rpc Update(StreamRegistryServiceUpdateRequest) returns (StreamRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/stream/schema/{stream.metadata.group}/{stream.metadata.name}"
      body: "*"
    };
  }

  rpc Delete(StreamRegistryServiceDeleteRequest) returns (StreamRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/stream/schema/{metadata.group}/{metadata.name}"};
  }

  rpc Get(StreamRegistryServiceGetRequest) returns (StreamRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/stream/schema/{metadata.group}/{metadata.name}"};
  }

  rpc List(StreamRegistryServiceListRequest) returns (StreamRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/stream/schema/lists/{group}"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(StreamRegistryServiceExistRequest) returns (StreamRegistryServiceExistResponse);
}

message IndexRuleBindingRegistryServiceCreateRequest {
  banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
}

message IndexRuleBindingRegistryServiceCreateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message IndexRuleBindingRegistryServiceUpdateRequest {
  banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
}

message IndexRuleBindingRegistryServiceUpdateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message IndexRuleBindingRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message IndexRuleBindingRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message IndexRuleBindingRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message IndexRuleBindingRegistryServiceGetResponse {
  banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
}

message IndexRuleBindingRegistryServiceListRequest {
  string group = 1;
}

message IndexRuleBindingRegistryServiceListResponse {
  repeated banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
}

message IndexRuleBindingRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message IndexRuleBindingRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_index_rule_binding = 2;
}

service IndexRuleBindingRegistryService {
  rpc Create(IndexRuleBindingRegistryServiceCreateRequest) returns (IndexRuleBindingRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/index-rule-binding/schema"
      body: "*"
    };
  }

  rpc Update(IndexRuleBindingRegistryServiceUpdateRequest) returns (IndexRuleBindingRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/index-rule-binding/schema/{index_rule_binding.metadata.group}/{index_rule_binding.metadata.name}"
      body: "*"
    };
  }

  rpc Delete(IndexRuleBindingRegistryServiceDeleteRequest) returns (IndexRuleBindingRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/index-rule-binding/schema/{metadata.group}/{metadata.name}"};
  }

  rpc Get(IndexRuleBindingRegistryServiceGetRequest) returns (IndexRuleBindingRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/index-rule-binding/schema/{metadata.group}/{metadata.name}"};
  }

  rpc List(IndexRuleBindingRegistryServiceListRequest) returns (IndexRuleBindingRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/index-rule-binding/schema/lists/{group}"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(IndexRuleBindingRegistryServiceExistRequest) returns (IndexRuleBindingRegistryServiceExistResponse);
}

message IndexRuleRegistryServiceCreateRequest {
  banyandb.database.v1.IndexRule index_rule = 1;
}

message IndexRuleRegistryServiceCreateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message IndexRuleRegistryServiceUpdateRequest {
  banyandb.database.v1.IndexRule index_rule = 1;
}

message IndexRuleRegistryServiceUpdateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message IndexRuleRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message IndexRuleRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message IndexRuleRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message IndexRuleRegistryServiceGetResponse {
  banyandb.database.v1.IndexRule index_rule = 1;
}

message IndexRuleRegistryServiceListRequest {
  string group = 1;
}

message IndexRuleRegistryServiceListResponse {
  repeated banyandb.database.v1.IndexRule index_rule = 1;
}

message IndexRuleRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message IndexRuleRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_index_rule = 2;
}

service IndexRuleRegistryService {
  rpc Create(IndexRuleRegistryServiceCreateRequest) returns (IndexRuleRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/index-rule/schema"
      body: "*"
    };
  }

  rpc Update(IndexRuleRegistryServiceUpdateRequest) returns (IndexRuleRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/index-rule/schema/{index_rule.metadata.group}/{index_rule.metadata.name}"
      body: "*"
    };
  }

  rpc Delete(IndexRuleRegistryServiceDeleteRequest) returns (IndexRuleRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/index-rule/schema/{metadata.group}/{metadata.name}"};
  }

  rpc Get(IndexRuleRegistryServiceGetRequest) returns (IndexRuleRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/index-rule/schema/{metadata.group}/{metadata.name}"};
  }

  rpc List(IndexRuleRegistryServiceListRequest) returns (IndexRuleRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/index-rule/schema/lists/{group}"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(IndexRuleRegistryServiceExistRequest) returns (IndexRuleRegistryServiceExistResponse);
}

message MeasureRegistryServiceCreateRequest {
  banyandb.database.v1.Measure measure = 1;
}

message MeasureRegistryServiceCreateResponse {
  int64 mod_revision = 1;
}

message MeasureRegistryServiceUpdateRequest {
  banyandb.database.v1.Measure measure = 1;
}

message MeasureRegistryServiceUpdateResponse {
  int64 mod_revision = 1;
}

message MeasureRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message MeasureRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message MeasureRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message MeasureRegistryServiceGetResponse {
  banyandb.database.v1.Measure measure = 1;
}

message MeasureRegistryServiceListRequest {
  string group = 1;
}

message MeasureRegistryServiceListResponse {
  repeated banyandb.database.v1.Measure measure = 1;
}

message MeasureRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message MeasureRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_measure = 2;
}

service MeasureRegistryService {
  rpc Create(MeasureRegistryServiceCreateRequest) returns (MeasureRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/measure/schema"
      body: "*"
    };
  }

  rpc Update(MeasureRegistryServiceUpdateRequest) returns (MeasureRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/measure/schema/{measure.metadata.group}/{measure.metadata.name}"
      body: "*"
    };
  }

  rpc Delete(MeasureRegistryServiceDeleteRequest) returns (MeasureRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/measure/schema/{metadata.group}/{metadata.name}"};
  }

  rpc Get(MeasureRegistryServiceGetRequest) returns (MeasureRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/measure/schema/{metadata.group}/{metadata.name}"};
  }

  rpc List(MeasureRegistryServiceListRequest) returns (MeasureRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/measure/schema/lists/{group}"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(MeasureRegistryServiceExistRequest) returns (MeasureRegistryServiceExistResponse);
}

message GroupRegistryServiceCreateRequest {
  banyandb.common.v1.Group group = 1;
}

message GroupRegistryServiceCreateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message GroupRegistryServiceUpdateRequest {
  banyandb.common.v1.Group group = 1;
}

message GroupRegistryServiceUpdateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

// GroupRegistryServiceDeleteRequest is the request for deleting a group.
message GroupRegistryServiceDeleteRequest {
  // group is the name of the group to delete.
  string group = 1;
  // dry_run indicates whether to perform a dry run without actually deleting data.
  // When true, returns what would be deleted without making changes.
  bool dry_run = 2;
  // force indicates whether to force delete the group even if it contains data.
  // When false, deletion will fail if the group is not empty.
  bool force = 3;
  // data_only indicates whether to delete only data files without removing metadata.
  // When true, metadata are preserved.
  bool data_only = 4;
}

// GroupRegistryServiceDeleteResponse is the response for deleting a group.
message GroupRegistryServiceDeleteResponse {
  // schema_info contains the schema resources that would be deleted (populated in dry-run mode).
  SchemaInfo schema_info = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

// GroupDeletionTask represents the status of a group deletion operation.
message GroupDeletionTask {
  // Phase represents the current phase of the deletion task.
  enum Phase {
    PHASE_UNSPECIFIED = 0;
    // PHASE_PENDING indicates the task is waiting to start.
    PHASE_PENDING = 1;
    // PHASE_IN_PROGRESS indicates the task is currently executing.
    PHASE_IN_PROGRESS = 2;
    // PHASE_COMPLETED indicates the task has completed successfully.
    PHASE_COMPLETED = 3;
    // PHASE_FAILED indicates the task has failed.
    PHASE_FAILED = 4;
  }
  // current_phase is the current phase of the deletion task.
  Phase current_phase = 1;
  // total_counts maps resource types to their total count.
  map<string, int32> total_counts = 2;
  // deleted_counts maps resource types to the count of deleted resources.
  map<string, int32> deleted_counts = 3;
  // total_data_size_bytes is the total size of data to be deleted in bytes.
  int64 total_data_size_bytes = 4;
  // deleted_data_size_bytes is the size of data that has been deleted in bytes.
  int64 deleted_data_size_bytes = 5;
  // message provides additional information about the task status.
  string message = 6;
  // created_at is the timestamp when the task was created.
  google.protobuf.Timestamp created_at = 7;
  // updated_at is the timestamp when the task was last updated.
  google.protobuf.Timestamp updated_at = 8;
}

// GroupRegistryServiceQueryRequest is the request for querying a group deletion task.
message GroupRegistryServiceQueryRequest {
  // group is the name of the group whose deletion task to query.
  string group = 1;
}

// GroupRegistryServiceQueryResponse is the response for querying a group deletion task.
message GroupRegistryServiceQueryResponse {
  // task contains the status of the deletion task.
  GroupDeletionTask task = 1;
}

message GroupRegistryServiceGetRequest {
  string group = 1;
}

message GroupRegistryServiceGetResponse {
  banyandb.common.v1.Group group = 1;
}

message GroupRegistryServiceListRequest {}

message GroupRegistryServiceListResponse {
  repeated banyandb.common.v1.Group group = 1;
}

message GroupRegistryServiceExistRequest {
  string group = 1;
}

message GroupRegistryServiceExistResponse {
  bool has_group = 1;
}

// GroupRegistryServiceInspectRequest is the request for inspecting a group's detailed information.
message GroupRegistryServiceInspectRequest {
  // group is the name of the group to inspect.
  string group = 1;
}

// GroupRegistryServiceInspectResponse is the response for inspecting a group.
message GroupRegistryServiceInspectResponse {
  // group contains the group metadata.
  banyandb.common.v1.Group group = 1;
  // schema_info contains information about all schemas in the group.
  SchemaInfo schema_info = 2;
  // data_info contains data storage information for each node.
  repeated DataInfo data_info = 3;
  // liaison_info contains information about pending operations in liaison.
  repeated LiaisonInfo liaison_info = 4;
}

// SchemaInfo contains information about all schema objects in a group.
message SchemaInfo {
  // streams is the list of stream names in the group.
  repeated string streams = 1;
  // measures is the list of measure names in the group.
  repeated string measures = 2;
  // traces is the list of trace names in the group.
  repeated string traces = 3;
  // properties is the list of property names in the group.
  repeated string properties = 4;
  // index_rules is the list of index rule names in the group.
  repeated string index_rules = 5;
  // index_rule_bindings is the list of index rule binding names in the group.
  repeated string index_rule_bindings = 6;
  // topn_aggregations is the list of TopN aggregation names in the group.
  repeated string topn_aggregations = 7;
}

// DataInfo contains data storage information for a specific node.
message DataInfo {
  // node is the node that stores this data.
  banyandb.database.v1.Node node = 1;
  // segment_info contains information about each segment on this node.
  repeated SegmentInfo segment_info = 2;
  // data_size_bytes is the total size of data on this node in bytes.
  int64 data_size_bytes = 3;
}

// SegmentInfo contains information about a specific time segment.
message SegmentInfo {
  // segment_id is the unique identifier of the segment.
  string segment_id = 1;
  // time_range_start is the start time of the segment.
  string time_range_start = 2;
  // time_range_end is the end time of the segment.
  string time_range_end = 3;
  // shard_info contains information about each shard in this segment.
  repeated ShardInfo shard_info = 4;
  // series_index_info contains information about the series index.
  SeriesIndexInfo series_index_info = 5;
}

// ShardInfo contains information about a specific shard.
message ShardInfo {
  // shard_id is the unique identifier of the shard.
  uint32 shard_id = 1;
  // data_count is the number of data entries in this shard.
  int64 data_count = 2;
  // data_size_bytes is the size of data in this shard in bytes.
  int64 data_size_bytes = 3;
  // part_count is the number of parts in this shard.
  int64 part_count = 4;
  // inverted_index_info contains information about the inverted index.
  InvertedIndexInfo inverted_index_info = 5;
  // sidx_info contains information about sidx.
  SIDXInfo sidx_info = 6;
  // file_part_count is the number of file parts (excluding in-memory parts) in this shard.
  int64 file_part_count = 7;
}

// SeriesIndexInfo contains information about the series index.
message SeriesIndexInfo {
  // data_count is the number of entries in the series index.
  int64 data_count = 1;
  // data_size_bytes is the size of the series index in bytes.
  int64 data_size_bytes = 2;
}

// InvertedIndexInfo contains information about the inverted index.
message InvertedIndexInfo {
  // data_count is the number of entries in the inverted index.
  int64 data_count = 1;
  // data_size_bytes is the size of the inverted index in bytes.
  int64 data_size_bytes = 2;
}

// SIDXInfo contains information about sidx.
message SIDXInfo {
  // data_count is the number of entries in sidx.
  int64 data_count = 1;
  // data_size_bytes is the size of sidx in bytes.
  int64 data_size_bytes = 2;
  // part_count is the number of parts in sidx.
  int64 part_count = 3;
}

// LiaisonInfo contains information about pending operations in liaison.
message LiaisonInfo {
  // pending_write_data_count is the number of data entries waiting to be written.
  int64 pending_write_data_count = 1;
  // pending_sync_part_count is the number of parts waiting to be synchronized.
  int64 pending_sync_part_count = 2;
  // pending_sync_data_size_bytes is the size of data waiting to be synchronized in bytes.
  int64 pending_sync_data_size_bytes = 3;
  // pending_handoff_part_count is the number of parts waiting for handoff.
  int64 pending_handoff_part_count = 4;
  // pending_handoff_data_size_bytes is the size of data waiting for handoff in bytes.
  int64 pending_handoff_data_size_bytes = 5;
}

service GroupRegistryService {
  rpc Create(GroupRegistryServiceCreateRequest) returns (GroupRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/group/schema"
      body: "*"
    };
  }

  rpc Update(GroupRegistryServiceUpdateRequest) returns (GroupRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/group/schema/{group.metadata.name}"
      body: "*"
    };
  }

  // Delete removes a group and all its associated data.
  // Supports dry-run mode to preview what would be deleted.
  rpc Delete(GroupRegistryServiceDeleteRequest) returns (GroupRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/group/schema/{group}"};
  }

  rpc Get(GroupRegistryServiceGetRequest) returns (GroupRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/group/schema/{group}"};
  }

  rpc List(GroupRegistryServiceListRequest) returns (GroupRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/group/schema/lists"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(GroupRegistryServiceExistRequest) returns (GroupRegistryServiceExistResponse);

  // Inspect retrieves detailed information about a group including its schemas, data distribution, and pending operations.
  rpc Inspect(GroupRegistryServiceInspectRequest) returns (GroupRegistryServiceInspectResponse) {
    option (google.api.http) = {get: "/v1/group/content/{group}"};
  }

  // Query retrieves the status of a group deletion task.
  rpc Query(GroupRegistryServiceQueryRequest) returns (GroupRegistryServiceQueryResponse) {
    option (google.api.http) = {get: "/v1/group/task/{group}"};
  }
}

message TopNAggregationRegistryServiceCreateRequest {
  banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
}

message TopNAggregationRegistryServiceCreateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message TopNAggregationRegistryServiceUpdateRequest {
  banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
}

message TopNAggregationRegistryServiceUpdateResponse {
  // mod_revision is the etcd revision assigned by the server on successful create/update.
  int64 mod_revision = 1;
}

message TopNAggregationRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message TopNAggregationRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message TopNAggregationRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message TopNAggregationRegistryServiceGetResponse {
  banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
}

message TopNAggregationRegistryServiceListRequest {
  string group = 1;
}

message TopNAggregationRegistryServiceListResponse {
  repeated banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
}

message TopNAggregationRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message TopNAggregationRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_top_n_aggregation = 2;
}

service TopNAggregationRegistryService {
  rpc Create(TopNAggregationRegistryServiceCreateRequest) returns (TopNAggregationRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/topn-agg/schema"
      body: "*"
    };
  }
  rpc Update(TopNAggregationRegistryServiceUpdateRequest) returns (TopNAggregationRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/topn-agg/schema/{top_n_aggregation.metadata.group}/{top_n_aggregation.metadata.name}"
      body: "*"
    };
  }
  rpc Delete(TopNAggregationRegistryServiceDeleteRequest) returns (TopNAggregationRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/topn-agg/schema/{metadata.group}/{metadata.name}"};
  }
  rpc Get(TopNAggregationRegistryServiceGetRequest) returns (TopNAggregationRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/topn-agg/schema/{metadata.group}/{metadata.name}"};
  }
  rpc List(TopNAggregationRegistryServiceListRequest) returns (TopNAggregationRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/topn-agg/schema/lists/{group}"};
  }
  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(TopNAggregationRegistryServiceExistRequest) returns (TopNAggregationRegistryServiceExistResponse);
}

message SnapshotRequest {
  message Group {
    common.v1.Catalog catalog = 1;
    string group = 2;
  }
  repeated Group groups = 1;
}

message Snapshot {
  common.v1.Catalog catalog = 1;
  string name = 2;
  string error = 3;
}

message SnapshotResponse {
  repeated Snapshot snapshots = 1;
}

service SnapshotService {
  rpc Snapshot(SnapshotRequest) returns (SnapshotResponse) {
    option (google.api.http) = {
      get: "/v1/snapshot"
      additional_bindings: {
        post: "/v1/snapshot"
        body: "*"
      }
    };
  }
}

message PropertyRegistryServiceCreateRequest {
  banyandb.database.v1.Property property = 1;
}

message PropertyRegistryServiceCreateResponse {
  int64 mod_revision = 1;
}

message PropertyRegistryServiceUpdateRequest {
  banyandb.database.v1.Property property = 1;
}

message PropertyRegistryServiceUpdateResponse {
  int64 mod_revision = 1;
}

message PropertyRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message PropertyRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message PropertyRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message PropertyRegistryServiceGetResponse {
  banyandb.database.v1.Property property = 1;
}

message PropertyRegistryServiceListRequest {
  string group = 1;
}

message PropertyRegistryServiceListResponse {
  repeated banyandb.database.v1.Property properties = 1;
}

message PropertyRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message PropertyRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_property = 2;
}

service PropertyRegistryService {
  rpc Create(PropertyRegistryServiceCreateRequest) returns (PropertyRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/property/schema"
      body: "*"
    };
  }

  rpc Update(PropertyRegistryServiceUpdateRequest) returns (PropertyRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/property/schema/{property.metadata.group}/{property.metadata.name}"
      body: "*"
    };
  }

  rpc Delete(PropertyRegistryServiceDeleteRequest) returns (PropertyRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/property/schema/{metadata.group}/{metadata.name}"};
  }

  rpc Get(PropertyRegistryServiceGetRequest) returns (PropertyRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/property/schema/{metadata.group}/{metadata.name}"};
  }

  rpc List(PropertyRegistryServiceListRequest) returns (PropertyRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/property/schema/lists/{group}"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(PropertyRegistryServiceExistRequest) returns (PropertyRegistryServiceExistResponse);
}

message TraceRegistryServiceCreateRequest {
  banyandb.database.v1.Trace trace = 1;
}

message TraceRegistryServiceCreateResponse {
  int64 mod_revision = 1;
}

message TraceRegistryServiceUpdateRequest {
  banyandb.database.v1.Trace trace = 1;
}

message TraceRegistryServiceUpdateResponse {
  int64 mod_revision = 1;
}

message TraceRegistryServiceDeleteRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message TraceRegistryServiceDeleteResponse {
  bool deleted = 1;
  // delete_time is the server-assigned tombstone timestamp in unix nanos.
  int64 delete_time = 2;
  // mod_revision is the etcd revision of the tombstone; zero if the server did not record one.
  int64 mod_revision = 3;
}

message TraceRegistryServiceGetRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message TraceRegistryServiceGetResponse {
  banyandb.database.v1.Trace trace = 1;
}

message TraceRegistryServiceExistRequest {
  banyandb.common.v1.Metadata metadata = 1;
}

message TraceRegistryServiceExistResponse {
  bool has_group = 1;
  bool has_trace = 2;
}

message TraceRegistryServiceListRequest {
  string group = 1;
}

message TraceRegistryServiceListResponse {
  repeated banyandb.database.v1.Trace trace = 1;
}

service TraceRegistryService {
  rpc Create(TraceRegistryServiceCreateRequest) returns (TraceRegistryServiceCreateResponse) {
    option (google.api.http) = {
      post: "/v1/trace/schema"
      body: "*"
    };
  }

  rpc Update(TraceRegistryServiceUpdateRequest) returns (TraceRegistryServiceUpdateResponse) {
    option (google.api.http) = {
      put: "/v1/trace/schema/{trace.metadata.group}/{trace.metadata.name}"
      body: "*"
    };
  }

  rpc Delete(TraceRegistryServiceDeleteRequest) returns (TraceRegistryServiceDeleteResponse) {
    option (google.api.http) = {delete: "/v1/trace/schema/{metadata.group}/{metadata.name}"};
  }

  rpc Get(TraceRegistryServiceGetRequest) returns (TraceRegistryServiceGetResponse) {
    option (google.api.http) = {get: "/v1/trace/schema/{metadata.group}/{metadata.name}"};
  }

  rpc List(TraceRegistryServiceListRequest) returns (TraceRegistryServiceListResponse) {
    option (google.api.http) = {get: "/v1/trace/schema/lists/{group}"};
  }

  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch Get instead
  rpc Exist(TraceRegistryServiceExistRequest) returns (TraceRegistryServiceExistResponse);
}

message GetCurrentNodeRequest {}

message GetCurrentNodeResponse {
  banyandb.database.v1.Node node = 1;
}

service NodeQueryService {
  rpc GetCurrentNode(GetCurrentNodeRequest) returns (GetCurrentNodeResponse) {}
}

message GetClusterStateRequest {}

// RouteTable represents a collection of nodes grouped by their health state.
// It provides a view of nodes that are registered, actively healthy, and those being evicted.
message RouteTable {
  // registered contains all nodes that have been discovered and registered in this route.
  repeated banyandb.database.v1.Node registered = 1;
  // active contains node names (Node.Metadata.Name) that are currently healthy and can handle requests.
  repeated string active = 2;
  // evictable contains node names (Node.Metadata.Name) that are unhealthy and being retried before eviction.
  repeated string evictable = 3;
}

message GetClusterStateResponse {
  // Liaison node: map's key could be "tire1" and "tire2". tire1 route traffic between liaison nodes, tire2 spread data among data nodes
  // Data node: map's key could be "property" for gossip.
  // Lifecycle agent: map's key could be the next stage's name.
  map<string, RouteTable> route_tables = 1;
}

service ClusterStateService {
  rpc GetClusterState(GetClusterStateRequest) returns (GetClusterStateResponse) {
    option (google.api.http) = {get: "/v1/cluster/state"};
  }
}
