syntax = "proto3";

package coda.search.v1;

option java_package = "coda.search.v1";
option java_outer_classname = "SearchProto";

service SearchService {
  rpc SearchGraphQL(SearchGraphQLRequest)         returns (SearchGraphQLResponse);
  rpc SearchSnowflake(SearchSnowflakeRequest)     returns (SearchSnowflakeResponse);
  rpc ReportUsage(ReportUsageRequest)             returns (ReportUsageResponse);
  rpc GetGraphQLSchema(GetGraphQLSchemaRequest)   returns (GetGraphQLSchemaResponse);
  rpc GetSnowflakeSchema(GetSnowflakeSchemaRequest) returns (GetSnowflakeSchemaResponse);
  rpc FindJoinPath(FindJoinPathRequest)           returns (FindJoinPathResponse);
  rpc GetNodeNeighbors(GetNodeNeighborsRequest)   returns (GetNodeNeighborsResponse);
  rpc GetNodeDetail(GetNodeDetailRequest)         returns (GetNodeDetailResponse);
}

message SearchGraphQLRequest {
  string query = 1;
  int32  limit = 2;
  string cursor = 3;
}

message SearchGraphQLResponse {
  string                     query_id      = 1;
  repeated QueryFieldResult  queries       = 2;
  repeated TypeResult        types         = 3;
  repeated GraphNeighborhood neighborhoods = 4;
  string                     next_cursor   = 5;
}

message QueryFieldResult {
  string name        = 1;
  string description = 2;
  float  score       = 3;
  repeated ArgInfo args        = 4;
  string return_type = 5;
  string signature   = 6;
}

message TypeResult {
  string name        = 1;
  string description = 2;
  float  score       = 3;
}

message GraphNeighborhood {
  repeated GraphNode nodes = 1;
}

message GraphNode {
  string id   = 1;
  string kind = 2;
}

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

message SearchSnowflakeRequest {
  string query    = 1;
  string database = 2;
  int32  limit    = 3;
  string cursor   = 4;
}

message SearchSnowflakeResponse {
  string                query_id        = 1;
  repeated TableResult  tables          = 2;
  repeated ColumnResult columns         = 3;
  repeated TableResult  related_tables  = 4;
  string                next_cursor     = 5;
  repeated JoinPath     join_paths      = 6;
}

message TableResult {
  string database   = 1;
  string schema     = 2;
  string name       = 3;
  string comment    = 4;
  float  score      = 5;
  string last_modified = 6;
}

message ColumnResult {
  string database   = 1;
  string schema     = 2;
  string table_name = 3;
  string name       = 4;
  string type       = 5;
  string comment    = 6;
  float  score      = 7;
}

message JoinPath {
  string from_table = 1;
  string to_table   = 2;
  repeated string path      = 3;
  repeated string relations = 4;
}

message ReportUsageRequest {
  string          query_id     = 1;
  repeated string selected_ids = 2;
}

message ReportUsageResponse {}

// --- Schema download (conditional-GET pattern) ---

message GetGraphQLSchemaRequest {
  string known_hash = 1;
}

message GetGraphQLSchemaResponse {
  string hash = 1;
  bool changed = 2;
  string introspection_json = 3;
}

message GetSnowflakeSchemaRequest {
  string known_hash = 1;
}

message GetSnowflakeSchemaResponse {
  string hash = 1;
  bool changed = 2;
  repeated SnowflakeSchemaTable tables = 3;
}

message SnowflakeSchemaTable {
  string fqn = 1;
  string database = 2;
  string schema = 3;
  string name = 4;
  string kind = 5;
  string comment = 6;
  int64 row_count = 7;
  repeated SnowflakeSchemaColumn columns = 8;
}

message SnowflakeSchemaColumn {
  string name = 1;
  string type = 2;
  bool nullable = 3;
  string comment = 4;
}

// --- Graph exploration (agent-facing) ---

message FindJoinPathRequest {
  string source    = 1; // Engine name: "snowflake", "graphql"
  string from_id   = 2; // Source node FQN
  string to_id     = 3; // Target node FQN
  int32  max_depth = 4; // Max FK hops (default 5, max 10)
}

message FindJoinPathResponse {
  bool            found     = 1;
  repeated string path      = 2; // Ordered node IDs from source to target
  repeated string relations = 3; // FK column names along the path
  string          join_hint = 4; // SQL JOIN clause hint
}

message GetNodeNeighborsRequest {
  string source    = 1; // Engine name
  string node_id   = 2; // Node FQN
  int32  depth     = 3; // BFS depth (default 1, max 3)
  string direction = 4; // "out", "in", "both" (default "both")
}

message GetNodeNeighborsResponse {
  NodeInfo          node      = 1;
  repeated Neighbor neighbors = 2;
}

message NodeInfo {
  string id     = 1;
  string kind   = 2;
  int32  degree = 3;
}

message Neighbor {
  string id        = 1;
  string kind      = 2;
  int32  degree    = 3;
  string relation  = 4;
  string direction = 5; // "out" or "in"
}

message GetNodeDetailRequest {
  string source  = 1; // Engine name
  string node_id = 2; // Node FQN
}

message GetNodeDetailResponse {
  string                       id              = 1;
  string                       kind            = 2;
  int32                        degree          = 3;
  string                       document        = 4; // Full text representation
  repeated string              keywords        = 5; // Pre-tokenized keywords
  repeated Neighbor            foreign_keys    = 6; // Direct FK neighbors
  repeated GlossaryTermMatch   glossary_matches = 7;
}

message GlossaryTermMatch {
  string          term    = 1;
  repeated string targets = 2;
  string          context = 3;
}
