syntax = "proto3";

option java_package = "com.soulfiremc.grpc.generated";
option java_multiple_files = true;

package soulfire.v1;

import "soulfire/api_docs.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/timestamp.proto";

// ============================================================================
// MetricsService - Instance Metrics and Monitoring Service
// ============================================================================
//
// The MetricsService provides real-time metrics and monitoring data for
// SoulFire instances. It enables clients to:
//
// - View time-series metrics (bot counts, network traffic, tick duration, etc.)
// - View current-state distributions (health histograms, dimension counts, etc.)
// - Track bot positions on an XZ scatter plot
//
// Metrics are collected server-side and stored in a ring buffer, ensuring
// consistent data across multiple clients. Snapshots are sampled every 3 seconds.
// ============================================================================

// A single time-series data point sampled at a fixed interval.
message MetricsSnapshot {
  // When this snapshot was taken.
  google.protobuf.Timestamp timestamp = 1 [(google.api.field_behavior) = REQUIRED];

  // Number of bots currently connected to the Minecraft server.
  uint32 bots_online = 2 [(google.api.field_behavior) = REQUIRED];
  // Total number of bots configured in the instance (online + offline).
  uint32 bots_total = 3 [(google.api.field_behavior) = REQUIRED];

  // Cumulative packets sent across all bots since the server started.
  uint64 packets_sent_total = 4 [(google.api.field_behavior) = REQUIRED];
  // Cumulative packets received across all bots since the server started.
  uint64 packets_received_total = 5 [(google.api.field_behavior) = REQUIRED];
  // Cumulative bytes sent across all bots since the server started.
  uint64 bytes_sent_total = 6 [(google.api.field_behavior) = REQUIRED];
  // Cumulative bytes received across all bots since the server started.
  uint64 bytes_received_total = 7 [(google.api.field_behavior) = REQUIRED];

  // Current rate of packets sent per second (computed from counter deltas).
  double packets_sent_per_second = 8 [(google.api.field_behavior) = REQUIRED];
  // Current rate of packets received per second (computed from counter deltas).
  double packets_received_per_second = 9 [(google.api.field_behavior) = REQUIRED];
  // Current rate of bytes sent per second (computed from counter deltas).
  double bytes_sent_per_second = 10 [(google.api.field_behavior) = REQUIRED];
  // Current rate of bytes received per second (computed from counter deltas).
  double bytes_received_per_second = 11 [(google.api.field_behavior) = REQUIRED];

  // Average tick duration across all bots in milliseconds.
  double avg_tick_duration_ms = 12 [(google.api.field_behavior) = REQUIRED];
  // Maximum tick duration observed across all bots in milliseconds.
  // Reset each sampling interval.
  double max_tick_duration_ms = 13 [(google.api.field_behavior) = REQUIRED];

  // Average health across all online bots with player data.
  // Range: 0 to 20 (Minecraft health points).
  double avg_health = 14 [(google.api.field_behavior) = REQUIRED];
  // Average food level across all online bots with player data.
  // Range: 0 to 20 (Minecraft food points).
  double avg_food_level = 15 [(google.api.field_behavior) = REQUIRED];
  // Total number of loaded chunks across all online bots.
  uint32 total_loaded_chunks = 16 [(google.api.field_behavior) = REQUIRED];
  // Total number of tracked entities across all online bots.
  uint32 total_tracked_entities = 17 [(google.api.field_behavior) = REQUIRED];

  // Connection events since the previous snapshot.
  uint32 connections = 18 [(google.api.field_behavior) = REQUIRED];
  // Disconnection events since the previous snapshot.
  uint32 disconnections = 19 [(google.api.field_behavior) = REQUIRED];
}

// Current-state distributions computed from the latest bot data.
// These are not time-series; they reflect the current instant.
message MetricsDistributions {
  // Health histogram with 10 buckets of width 2: [0,2), [2,4), ..., [18,20].
  // Each entry is the count of bots whose health falls in that bucket.
  repeated uint32 health_histogram = 1;
  // Food level histogram with 10 buckets of width 2: [0,2), [2,4), ..., [18,20].
  repeated uint32 food_histogram = 2;
  // Number of bots in each dimension (e.g., "minecraft:overworld" -> 5).
  map<string, uint32> dimension_counts = 3;
  // Number of bots in each game mode (e.g., "SURVIVAL" -> 10).
  map<string, uint32> game_mode_counts = 4;
  // XZ positions of online bots for scatter plot rendering.
  repeated BotPosition bot_positions = 5;
}

// Position of a single bot on the XZ plane.
message BotPosition {
  // X coordinate in the Minecraft world.
  double x = 1 [(google.api.field_behavior) = REQUIRED];
  // Z coordinate in the Minecraft world.
  double z = 2 [(google.api.field_behavior) = REQUIRED];
  // Dimension the bot is in (e.g., "minecraft:overworld").
  string dimension = 3 [(google.api.field_behavior) = REQUIRED];
}

// Request to retrieve metrics for a specific instance.
message GetInstanceMetricsRequest {
  // The UUID of the SoulFire instance to get metrics for.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
  // If set, only return snapshots taken after this timestamp.
  // Used for incremental fetching to reduce payload size.
  optional google.protobuf.Timestamp since = 2;
}

// Response containing time-series metrics and current distributions.
message GetInstanceMetricsResponse {
  // Time-series snapshots ordered oldest to newest.
  // Contains up to 600 snapshots (30 minutes at 3-second intervals).
  // If the request included a "since" timestamp, only newer snapshots are returned.
  repeated MetricsSnapshot snapshots = 1 [(google.api.field_behavior) = REQUIRED];
  // Current-state distributions computed from the latest bot data.
  MetricsDistributions distributions = 2 [(google.api.field_behavior) = REQUIRED];
}

// A single server-level system metrics data point sampled at a fixed interval.
message ServerMetricsSnapshot {
  // When this snapshot was taken.
  google.protobuf.Timestamp timestamp = 1 [(google.api.field_behavior) = REQUIRED];

  // JVM process CPU load (0.0 to 1.0, or -1.0 if unavailable).
  double process_cpu_load = 2 [(google.api.field_behavior) = REQUIRED];
  // System-wide CPU load (0.0 to 1.0, or -1.0 if unavailable).
  double system_cpu_load = 3 [(google.api.field_behavior) = REQUIRED];

  // Heap memory currently used in bytes.
  uint64 heap_used_bytes = 4 [(google.api.field_behavior) = REQUIRED];
  // Heap memory committed (reserved by JVM) in bytes.
  uint64 heap_committed_bytes = 5 [(google.api.field_behavior) = REQUIRED];
  // Maximum heap memory in bytes (-1 if undefined).
  int64 heap_max_bytes = 6 [(google.api.field_behavior) = REQUIRED];
  // Non-heap memory currently used in bytes.
  uint64 non_heap_used_bytes = 7 [(google.api.field_behavior) = REQUIRED];

  // Current live thread count.
  uint32 thread_count = 8 [(google.api.field_behavior) = REQUIRED];
  // Current daemon thread count.
  uint32 daemon_thread_count = 9 [(google.api.field_behavior) = REQUIRED];

  // Cumulative GC collection count across all collectors.
  uint64 gc_collection_count = 10 [(google.api.field_behavior) = REQUIRED];
  // Cumulative GC collection time in milliseconds across all collectors.
  uint64 gc_collection_time_ms = 11 [(google.api.field_behavior) = REQUIRED];

  // JVM uptime in milliseconds.
  uint64 uptime_ms = 12 [(google.api.field_behavior) = REQUIRED];
  // Number of available processors.
  uint32 available_processors = 13 [(google.api.field_behavior) = REQUIRED];

  // Total bots online across all instances.
  uint32 total_bots_online = 14 [(google.api.field_behavior) = REQUIRED];
  // Total bots configured across all instances.
  uint32 total_bots_total = 15 [(google.api.field_behavior) = REQUIRED];
  // Total bots desired to be running across all instances.
  uint32 total_bots_desired = 17 [(google.api.field_behavior) = REQUIRED];
  // Instances with at least one desired bot.
  uint32 instances_with_desired_bots = 18 [(google.api.field_behavior) = REQUIRED];
  // Bots currently queued or starting.
  uint32 total_bots_starting = 19 [(google.api.field_behavior) = REQUIRED];
  // Bots waiting for an automatic reconnect.
  uint32 total_bots_retrying = 20 [(google.api.field_behavior) = REQUIRED];
  // Desired bots whose latest connection attempt failed.
  uint32 total_bots_failed = 21 [(google.api.field_behavior) = REQUIRED];
}

// Request to retrieve server-level system metrics.
message GetServerMetricsRequest {
  // If set, only return snapshots taken after this timestamp.
  // Used for incremental fetching to reduce payload size.
  optional google.protobuf.Timestamp since = 1;
}

// Response containing server-level system metrics time-series.
message GetServerMetricsResponse {
  // Time-series snapshots ordered oldest to newest.
  // Contains up to 600 snapshots (30 minutes at 3-second intervals).
  repeated ServerMetricsSnapshot snapshots = 1 [(google.api.field_behavior) = REQUIRED];
}

// Service for retrieving instance metrics and monitoring data.
// Metrics are collected server-side and stored in a ring buffer.
// All operations require appropriate permissions.
service MetricsService {
  // Returns time-series metrics and current distributions for an instance.
  // Supports incremental fetching via the "since" parameter.
  // Requires: READ_BOT_INFO permission on the instance
  // Errors: NOT_FOUND if instance does not exist
  rpc GetInstanceMetrics (GetInstanceMetricsRequest) returns (GetInstanceMetricsResponse) {
    option (google.api.http) = { get: "/v1/instances/{instance_id}/metrics" };
    option (soulfire.v1.api_method) = {
      display_name: "Get instance metrics"
      permissions: "READ_BOT_INFO"
      scope: "instance"
    };
  }

  // Returns server-level system metrics (CPU, memory, threads, GC, aggregate bots).
  // Supports incremental fetching via the "since" parameter.
  // Requires: READ_SERVER_CONFIG global permission
  rpc GetServerMetrics (GetServerMetricsRequest) returns (GetServerMetricsResponse) {
    option (google.api.http) = { get: "/v1/server/metrics" };
    option (soulfire.v1.api_method) = {
      display_name: "Get server metrics"
      permissions: "READ_SERVER_CONFIG"
      scope: "server"
    };
  }
}
