syntax = "proto3";

package coda.search.v1;

import "coda/search/v1/search.proto";

service AdminService {
  rpc GetEngines(GetEnginesRequest) returns (GetEnginesResponse);
  rpc ListSnapshots(ListSnapshotsRequest) returns (ListSnapshotsResponse);
  rpc RollbackSnapshot(RollbackSnapshotRequest) returns (RollbackSnapshotResponse);
  rpc RefreshEngine(RefreshEngineRequest) returns (RefreshEngineResponse);
  rpc TraceQuery(TraceQueryRequest) returns (TraceQueryResponse);
  rpc GetGlossary(GetGlossaryRequest) returns (GetGlossaryResponse);
  rpc UpdateGlossary(UpdateGlossaryRequest) returns (UpdateGlossaryResponse);
  rpc GetConfig(GetConfigRequest) returns (GetConfigResponse);
  rpc StreamEvents(StreamEventsRequest) returns (stream SearchEventMessage);
  rpc GetGraphSummary(GetGraphSummaryRequest) returns (GetGraphSummaryResponse);
  rpc GetSubgraph(GetSubgraphRequest) returns (GetSubgraphResponse);
}

message GetEnginesRequest {}
message GetEnginesResponse { repeated EngineInfo engines = 1; }
message EngineInfo {
  string name = 1;
  bool ready = 2;
  bool degraded = 3;
  string hash = 4;
  int32 document_count = 5;
  int32 vector_count = 6;
  string last_poll_at = 7;
  int32 last_poll_duration_ms = 8;
  // Field 9 reserved — was circuit_breaker (service-level, not per-engine)
  reserved 9;
}

message ListSnapshotsRequest { string engine_name = 1; }
message ListSnapshotsResponse { repeated SnapshotInfo snapshots = 1; }
message SnapshotInfo { string key = 1; string timestamp = 2; }

message RollbackSnapshotRequest { string engine_name = 1; string snapshot_key = 2; }
message RollbackSnapshotResponse { bool success = 1; }

message RefreshEngineRequest { string engine_name = 1; }
message RefreshEngineResponse { bool accepted = 1; }

message TraceQueryRequest { string query = 1; string source = 2; int32 limit = 3; }
message TraceQueryResponse {
  QueryTrace trace = 1;
  repeated JoinPath join_paths = 2;
}
message QueryTrace { string trace_id = 1; int32 total_duration_ms = 2; repeated TraceSpan spans = 3; }
message TraceSpan {
  string name = 1;
  int32 duration_ms = 2;
  repeated ScoredDoc documents = 3;
  KeywordDetail keyword_detail = 10;
  VectorDetail vector_detail = 11;
  GlossaryDetail glossary_detail = 12;
  FusionDetail fusion_detail = 13;
  RerankDetail rerank_detail = 14;
  AugmentDetail augment_detail = 15;
}
message ScoredDoc { string id = 1; double score = 2; int32 rank = 3; }
message KeywordDetail { repeated string tokens = 1; repeated string expanded_tokens = 2; }
message VectorDetail { int32 candidates_searched = 1; double top_similarity = 2; }
message GlossaryDetail { repeated GlossaryMatchInfo matches = 1; }
message GlossaryMatchInfo { string term = 1; repeated string targets = 2; repeated string related = 3; bool fuzzy = 4; int32 edit_distance = 5; }
message FusionDetail { string method = 1; int32 k = 2; int32 input_count = 3; }
message RerankDetail { int32 input_count = 1; repeated RerankDelta deltas = 2; }
message RerankDelta { string id = 1; int32 rank_before = 2; int32 rank_after = 3; double score_before = 4; double score_after = 5; }
message AugmentDetail { repeated AugmentedNodeInfo nodes = 1; }
message AugmentedNodeInfo { string id = 1; string reached_from = 2; string relation = 3; }

message GetGlossaryRequest { string source = 1; }
message GetGlossaryResponse { repeated GlossaryEntryInfo entries = 1; }
message GlossaryEntryInfo { repeated string terms = 1; repeated string targets = 2; repeated string related = 3; string context = 4; string domain = 5; double priority = 6; }

message UpdateGlossaryRequest { string source = 1; repeated GlossaryEntryInfo add = 2; repeated GlossaryEntryInfo update = 3; repeated string remove_terms = 4; }
message UpdateGlossaryResponse { bool success = 1; int32 added = 2; int32 updated = 3; int32 removed = 4; }

message GetConfigRequest {}
message GetConfigResponse { map<string, string> config = 1; }

message StreamEventsRequest { string filter = 1; string source = 2; }
message SearchEventMessage {
  string type = 1;
  int64 seq = 2;
  string timestamp = 3;
  string source = 4;
  string operation_id = 5;
  string trace_id = 6;
  string span_id = 7;
  string parent_span_id = 8;
  int32 duration_ms = 9;
  string data_json = 10;
}

message GetGraphSummaryRequest {
  string source = 1;
  int32 top_n = 2;
}

message GetGraphSummaryResponse {
  repeated KindCount kind_counts = 1;
  int32 total_nodes = 2;
  int32 total_edges = 3;
  repeated GraphNodeInfo hub_nodes = 4;
  repeated GraphEdgeInfo hub_edges = 5;
}

message KindCount {
  string kind = 1;
  int32 count = 2;
}

message GetSubgraphRequest {
  string source = 1;
  string node_id = 2;
  int32 depth = 3;
}

message GetSubgraphResponse {
  repeated GraphNodeInfo nodes = 1;
  repeated GraphEdgeInfo edges = 2;
}
message GraphNodeInfo { string id = 1; string label = 2; string kind = 3; int32 degree = 4; }
message GraphEdgeInfo { string source = 1; string target = 2; string relation = 3; }
