// Copyright 2025 PingCAP, Inc.
//
// Licensed 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 tici;

import "schema.proto";

// import "gogoproto/gogo.proto";
// import "rustproto.proto";

// option (gogoproto.sizer_all) = true;
// option (gogoproto.marshaler_all) = true;
// option (gogoproto.unmarshaler_all) = true;
// option (gogoproto.goproto_unkeyed_all) = false;
// option (gogoproto.goproto_unrecognized_all) = false;
// option (gogoproto.goproto_sizecache_all) = false;
// option (rustproto.lite_runtime_all) = true;

enum ErrorCode {
  SUCCESS = 0;
  UNKNOWN_ERROR = 1;
  INVALID_ARGUMENTS = 2;
  TRY_AGAIN = 3;
  DEPRECATED = 4;
  NOT_LEADER = 5;

  SHARD_NOT_FOUND = 11;
  INDEX_NOT_FOUND = 12;
  WORKER_NOT_FOUND = 13;
  SHARD_NOT_SCHEDULED = 14;
  COMPACTION_NOT_FOUND = 15;
  FRAG_RANGE_MISMATCH = 16;
  INVALID_OP = 17;
  THROTTLED = 18;
  PROGRESS_UNWINDED = 19;
}

// Key range definition
message KeyRange {
  // Inclusive lower key bound
  bytes start_key = 1;
  // Exclusive upper key bound
  bytes end_key = 2;
}

// Shard local cache information
message ShardLocalCacheInfo {
  ShardManifestHeader shard = 1;
  repeated string local_cache_addrs = 2;
}

// Request to get shard local cache information
message GetShardLocalCacheRequest {
  // Table ID to filter shards
  int64 table_id = 1;
  // Index ID to filter shards
  int64 index_id = 2;
  uint32 keyspace_id = 3;
  repeated KeyRange key_ranges = 4;
  // at most `limit` shards can be returned
  int32 limit = 5;
}

// Response containing shard local cache information
message GetShardLocalCacheResponse {
  ErrorCode status = 1;
  repeated ShardLocalCacheInfo shard_local_cache_infos = 2;
}

service MetaService {
  // APIs for create indexes

  // CreateIndex creates a new index
  rpc CreateIndex(CreateIndexRequest) returns (CreateIndexResponse);

  // DropIndex removes an existing index
  rpc DropIndex(DropIndexRequest) returns (DropIndexResponse);

  // GetIndexProgress retrieves the current progress of an index build
  rpc GetIndexProgress(GetIndexProgressRequest)
      returns (GetIndexProgressResponse);

  // -----------------------------------------------------------------------------


  // Get shard local cache information
  rpc GetShardLocalCacheInfo(GetShardLocalCacheRequest)
      returns (GetShardLocalCacheResponse);

  // -----------------------------------------------------------------------------
  // APIs for importing data

  // Get a cloud storage prefix for importing data.
  // This will create a new import job with a unique job_id and a cloud storage
  // prefix for the import data. The caller should generate files under the
  // cloud storage prefix and call `FinishImportPartitionUpload` after uploading
  // each partition. If there is an existing running import job for the same
  // identifier, it will return the existing job_id.
  rpc GetImportStoragePrefix(GetImportStoragePrefixRequest)
      returns (GetImportStoragePrefixResponse);

  // Called by Import DXF to notify Meta Service after a partition for
  // the given index are uploaded.
  rpc FinishImportPartitionUpload(FinishImportPartitionUploadRequest)
      returns (FinishImportResponse);

  // Called before Add full index build to pre-split shards by
  // aggregated SortedKV metadata groups.
  rpc PreSplitImportShards(PreSplitImportShardsRequest)
      returns (PreSplitImportShardsResponse);

  // Called to notify Meta Service that the whole table/index upload is
  // finished.
  rpc FinishImportIndexUpload(FinishImportIndexUploadRequest)
      returns (FinishImportResponse);

  // -----------------------------------------------------------------------------
}

message ShardManifestHeader {
  uint64 shard_id = 1;
  bytes start_key = 2;
  bytes end_key = 3;
  uint64 epoch = 4;
  uint64 seq = 5;
}

message S3Location {
  string bucket = 1;
  string prefix = 2;
}

// CreateIndexRequest is a request to create an index
message CreateIndexRequest {
  string database_name = 1;
  bytes table_info = 2;
  int64 index_id = 3;
  // Keyspace ID
  uint32 keyspace_id = 4;

  schema.ParserInfo parser_info = 5;
}

// CreateIndexResponse is a response to the index creation request
message CreateIndexResponse {
  ErrorCode status = 1;
  // Error message, only valid when status is non-zero
  string error_message = 2;
  // Created index ID
  string index_id = 3;
}

// DropIndexRequest is a request to drop an index
message DropIndexRequest {
  int64 table_id = 1;
  int64 index_id = 2;
  // Keyspace ID
  uint32 keyspace_id = 3;
}

// DropIndexResponse is a response to the index drop request
message DropIndexResponse {
  ErrorCode status = 1;
  // Error message, only valid when status is non-zero
  string error_message = 2;
}

// GetIndexProgressRequest is a request to get the progress of an index build
message GetIndexProgressRequest {
  // Table ID
  int64 table_id = 1;
  // Index ID
  int64 index_id = 2;
  // Keyspace ID
  uint32 keyspace_id = 3;
}

// GetIndexProgressResponse only reports coarse-grained build state.
// Fine-grained import progress is tracked by the DDL layer.
message GetIndexProgressResponse {
  enum State {
    PENDING = 0;
    RUNNING = 1;
    COMPLETED = 2;
    FAILED = 3;
    NOTFOUND = 4;
    ERROR = 5;
  }
  ErrorCode status = 1;
  // Error message, only valid when status is non-zero
  string error_message = 2;
  // Build task state (PENDING, RUNNING, COMPLETED, FAILED)
  State state = 3;
}

// -----------------------------------------------------------------------------
// TiCI Full-text index data import workflow during Import Into (with DXF):
// -----------------------------------------------------------------------------
// 1. TiDB requests a cloud storage object prefix from the TiCI Meta Service.
//    It provides a unique tidb_task_id that specify a job in the TiDB side. A
//    job means one Import Into or Index Backfilling operation.
//
// 2. Import DXF generate the files for partition data and upload them to the
//    cloud storage prefix returned by TiCI. The files should contain the
//    table info, indexes info and the key-value pairs within a specific
//    key-range.
//
// 3. After import DXF has uploaded one partition data belonging to a baseline
//    full text index, it calls `FinishImportPartitionUpload`. This allows
//    TiCI to mark the partition as complete and make it available to downstream
//    consumers.
//
// 4. After import DXF has uploaded all partitions data belonging to a baseline
//    full text index on a job, it calls `FinishImportIndexUpload`.
//    This allows TiCI to mark the job as complete and make it available to
//    GC data after consumed by downstream consumers.
// -----------------------------------------------------------------------------

message GetImportStoragePrefixRequest {
  // TiDB unique task ID for this Import Into/Index Backfilling job.
  string tidb_task_id = 1;
  // Table ID of the target table.
  int64 table_id = 2;
  // Index ID of the target index. If this is an Import Into job that relates
  // to multiple indexes, this field should contain all the index IDs.
  repeated int64 index_ids = 3;
  // Keyspace ID
  uint32 keyspace_id = 4;
}

message GetImportStoragePrefixResponse {
  ErrorCode status = 1;
  // Optional human‑readable diagnostics, only defined when status is not
  // SUCCESS.
  string error_message = 2;
  // The import index job_id in TiCI
  uint64 job_id = 3;
  // The cloud storage prefix where the import data should be written.
  // The format is "s3://bucket/prefix/".
  string storage_uri = 4;
}

message FinishImportPartitionUploadRequest {
  // TiDB unique task ID for this Import Into/Index Backfilling job.
  string tidb_task_id = 1;
  // Key range for the partition uploaded.
  KeyRange key_range = 2;
  // The cloud storage path where the partition data is written.
  // The format is "s3://bucket/prefix/filename".
  string storage_uri = 3;
  // Keyspace ID
  uint32 keyspace_id = 4;
  // Index ID for the uploaded partition data. A zero value means unspecified.
  int64 index_id = 5;
}

// One merged SortedKVMeta group used for import pre-split analysis.
message PreSplitImportShardMeta {
  int64 ele_id = 1;
  bytes start_key = 2;
  bytes end_key = 3;
  uint64 total_kv_size = 4;
  uint64 total_kv_cnt = 5;
  int32 data_file_count = 6;
  int32 stat_file_count = 7;
}

message PreSplitImportShardsRequest {
  // TiDB unique task ID for this Import Into job.
  string tidb_task_id = 1;
  int64 table_id = 2;
  repeated int64 index_ids = 3;
  uint64 scan_snapshot_ts = 4;
  bytes start_key = 5;
  bytes end_key = 6;
  uint64 total_kv_size = 7;
  uint64 total_kv_cnt = 8;
  int32 data_file_count = 9;
  int32 stat_file_count = 10;
  repeated PreSplitImportShardMeta meta_groups = 11;
  uint32 keyspace_id = 12;
}

message PreSplitImportIndexResult {
  int64 table_id = 1;
  int64 index_id = 2;
  uint32 split_count = 3;
  uint32 shard_count_after_split = 4;
}

message PreSplitImportShardsResponse {
  ErrorCode status = 1;
  string error_message = 2;
  repeated bytes split_keys = 3;
  repeated PreSplitImportIndexResult index_results = 4;
}


message FinishImportIndexUploadRequest {
  // TiDB unique task ID for this Import Into/Index Backfilling job.
  string tidb_task_id = 1;
  // If status is SUCCESS, the job is finished successfully.
  // If status is not SUCCESS, the job is aborted or cancelled in TiDB side,
  // and TiCI should clean up all the related data.
  ErrorCode status = 2;
  // The error_message if the status is not SUCCESS.
  string error_message = 3;
  // Keyspace ID
  uint32 keyspace_id = 4;
}

message FinishImportResponse {
  ErrorCode status = 1;
  // Optional human‑readable diagnostics, only defined when status is not
  // SUCCESS.
  string error_message = 2;
}

// -----------------------------------------------------------------------------
