// SPDX-License-Identifier: AGPL-3.0-only

syntax = "proto3";

package planning;

import "gogoproto/gogo.proto";

option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;

message EncodedQueryPlan {
  EncodedQueryTimeRange timeRange = 1 [(gogoproto.nullable) = false];
  repeated EncodedNode nodes = 2;
  int64 rootNode = 3;

  // The original PromQL expression for this query.
  // May not accurately represent the query being executed if this query was built from a query plan representing a subexpression of a query.
  string originalExpression = 4;

  bool enableDelayedNameRemoval = 5;

  uint64 version = 6 [
    (gogoproto.casttype) = "QueryPlanVersion",
    (gogoproto.jsontag) = "version" // Override default value to remove 'omitempty'
  ];
}

message EncodedQueryTimeRange {
  int64 startT = 1; // Start timestamp, in milliseconds since Unix epoch.
  int64 endT = 2; // End timestamp, in milliseconds since Unix epoch.
  int64 intervalMilliseconds = 3; // Range query interval, or 1 for instant queries. Note that this is deliberately different to parser.EvalStmt.Interval for instant queries (where it is 0) to simplify some loop conditions.
  bool isInstant = 4;
}

message EncodedNode {
  // Why use this rather than a oneof field?
  // This allows us to define nodes in multiple packages without creating dependencies between them,
  // and allows us to define new kinds of nodes in other repositories (eg. the one used for Grafana Cloud Metrics).
  // Why use this rather than a Any value?
  // Any encodes the type name as a string (eg. 'type.googleapis.com/BinaryExpressionDetails'),
  // which means that the encoded form of an expression can be many times the size of the original PromQL expression.
  NodeType nodeType = 1;
  bytes details = 2;

  repeated int64 children = 3;

  // The following fields will only be populated if this plan was generated with descriptions enabled.
  string type = 4;
  string description = 5;
  repeated string childrenLabels = 6;
}

// FIXME: should we just use a generic integer for this? Adding this enum here seems to fly in the face of the
// decoupling goal of not using a oneof above.
enum NodeType {
  NODE_TYPE_UNKNOWN = 0;
  NODE_TYPE_VECTOR_SELECTOR = 1;
  NODE_TYPE_MATRIX_SELECTOR = 2;
  NODE_TYPE_AGGREGATE_EXPRESSION = 3;
  NODE_TYPE_BINARY_EXPRESSION = 4;
  NODE_TYPE_FUNCTION_CALL = 5;
  NODE_TYPE_NUMBER_LITERAL = 6;
  NODE_TYPE_STRING_LITERAL = 7;
  NODE_TYPE_UNARY_EXPRESSION = 8;
  NODE_TYPE_SUBQUERY = 9;

  NODE_TYPE_DUPLICATE = 10;
  NODE_TYPE_DEDUPLICATE_AND_MERGE = 11;

  NODE_TYPE_REMOTE_EXEC = 12;
  NODE_TYPE_TEST = 13;
  NODE_TYPE_DROP_NAME = 14;
  NODE_TYPE_STEP_INVARIANT_EXPRESSION = 15;
}
