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";

// Represents a single log entry from the SoulFire logging system.
// Log entries can be associated with specific instances, bots, or scripts,
// or they can be personal messages directed to a specific user.
message LogString {
  // Unique identifier for this log entry. Format is "{timestamp_millis}-{counter}"
  // where counter is an incrementing integer to ensure uniqueness within the same millisecond.
  string id = 1;

  // The formatted log message content. This is the fully formatted output including
  // any Minecraft formatting codes and exception stack traces if present.
  string message = 2;

  // The UUID of the instance this log is associated with, if any.
  // Present when the log was generated in the context of a specific attack instance.
  optional string instance_id = 3 [(soulfire.v1.api_field).format = "uuid"];

  // The UUID of the bot account this log is associated with, if any.
  // Present when the log was generated by or about a specific bot.
  optional string bot_account_id = 4 [(soulfire.v1.api_field).format = "uuid"];

  // The UUID of the script this log is associated with, if any.
  // Present when the log was generated during script execution.
  optional string script_id = 5 [(soulfire.v1.api_field).format = "uuid"];

  // Indicates whether this is a personal message directed to a specific user.
  // Personal messages are only visible to the target user and are not stored in the log history.
  bool personal = 6;

  // Human-readable name of the instance, if available.
  // Provided as a convenience to avoid requiring additional lookups.
  optional string instance_name = 7;

  // Human-readable name of the bot account, if available.
  // Provided as a convenience to avoid requiring additional lookups.
  optional string bot_account_name = 8;

  // The timestamp when this log entry was created.
  google.protobuf.Timestamp timestamp = 9;

  // The name of the logger that produced this log entry (e.g., "com.soulfiremc.server.SomeClass").
  // Useful for filtering and debugging purposes.
  optional string logger_name = 10;

  // The log level (e.g., "INFO", "WARN", "ERROR", "DEBUG", "TRACE", "FATAL").
  // Corresponds to Log4j2 log levels.
  optional string level = 11;
}

// Scope for receiving all non-personal logs from the entire server.
// Requires the GLOBAL_SUBSCRIBE_LOGS permission.
message GlobalLogScope {
}

// Scope for receiving logs from a specific attack instance.
// Only includes logs where the instance_id matches. Personal logs are excluded.
// Requires the INSTANCE_SUBSCRIBE_LOGS permission for the specified instance.
message InstanceLogScope {
  // The UUID of the instance to filter logs for. Must be a valid UUID string.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Scope for receiving logs from a specific bot within an instance.
// Only includes logs where both instance_id and bot_account_id match. Personal logs are excluded.
// Requires the INSTANCE_SUBSCRIBE_LOGS permission for the specified instance.
message BotLogScope {
  // The UUID of the instance the bot belongs to. Must be a valid UUID string.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];

  // The UUID of the bot account to filter logs for. Must be a valid UUID string.
  string bot_id = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Scope for receiving logs from a specific script running within an instance.
// Only includes logs where both instance_id and script_id match. Personal logs are excluded.
// Requires the INSTANCE_SUBSCRIBE_LOGS permission for the specified instance.
message InstanceScriptLogScope {
  // The UUID of the instance the script is running in. Must be a valid UUID string.
  string instance_id = 1 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];

  // The UUID of the script to filter logs for. Must be a valid UUID string.
  string script_id = 2 [(google.api.field_behavior) = REQUIRED, (soulfire.v1.api_field).format = "uuid"];
}

// Scope for receiving personal messages directed to the authenticated user.
// Personal messages are private notifications that are not stored in log history.
// No special permissions required beyond being authenticated.
message PersonalLogScope {
}

// Defines the scope of logs to retrieve or subscribe to.
// The scope determines which logs are included based on their source and the user's permissions.
message LogScope {
  // Reserved for removed GlobalScriptLogScope (field 4)
  oneof scope {
    // Receive all non-personal logs from the server. Requires GLOBAL_SUBSCRIBE_LOGS permission.
    GlobalLogScope global = 1;

    // Receive logs from a specific instance. Requires INSTANCE_SUBSCRIBE_LOGS permission.
    InstanceLogScope instance = 2;

    // Receive logs from a specific bot within an instance. Requires INSTANCE_SUBSCRIBE_LOGS permission.
    BotLogScope bot = 3;

    // Receive logs from a specific script within an instance. Requires INSTANCE_SUBSCRIBE_LOGS permission.
    InstanceScriptLogScope instance_script = 5;

    // Receive personal messages for the authenticated user. No special permissions required.
    PersonalLogScope personal = 6;
  }
}

// Request to retrieve historical log entries from the server's log buffer.
message PreviousLogRequest {
  // The scope defining which logs to retrieve. Personal logs are not included
  // in historical retrieval as they are not stored.
  LogScope scope = 4 [(google.api.field_behavior) = REQUIRED];

  // The maximum number of recent log entries to retrieve.
  // Must not exceed 300 (the server's maximum log buffer size).
  // Returns the newest entries that match the scope, up to this count.
  int32 count = 3 [(google.api.field_behavior) = REQUIRED];
}

// Response containing historical log entries.
message PreviousLogResponse {
  // The list of log entries matching the requested scope, ordered from oldest to newest.
  // May contain fewer entries than requested if not enough matching logs exist in the buffer.
  repeated LogString messages = 1 [(google.api.field_behavior) = REQUIRED];
}

// Request to subscribe to a real-time stream of log entries.
message LogRequest {
  // The scope defining which logs to receive. The subscription will only
  // deliver logs that match this scope and that the user has permission to access.
  LogScope scope = 3 [(google.api.field_behavior) = REQUIRED];
}

// Response containing a single log entry from a subscription stream.
message LogResponse {
  // A single log entry that matches the subscription scope.
  LogString message = 1 [(google.api.field_behavior) = REQUIRED];
}

// Service for accessing SoulFire server logs.
// Provides both historical log retrieval and real-time log streaming capabilities.
// All methods require appropriate authentication and permissions based on the requested scope.
service LogsService {
  // Retrieves historical log entries from the server's in-memory log buffer.
  // The server maintains a rolling buffer of the last 300 log entries.
  // Only returns non-personal logs that match the specified scope.
  //
  // Permissions required:
  // - GlobalLogScope: GLOBAL_SUBSCRIBE_LOGS
  // - InstanceLogScope/BotLogScope/InstanceScriptLogScope: INSTANCE_SUBSCRIBE_LOGS for the instance
  // - PersonalLogScope: No special permissions (but returns empty since personal logs are not stored)
  //
  // Errors:
  // - PERMISSION_DENIED: User lacks required permission for the requested scope
  // - INVALID_ARGUMENT: Scope not set or invalid UUID format
  // - INTERNAL: Server error while retrieving logs
  rpc GetPrevious (PreviousLogRequest) returns (PreviousLogResponse) {
    option (google.api.http) = {
      post: "/v1/logs:history"
      body: "*"
    };
    option (soulfire.v1.api_method) = {
      display_name: "Get log history"
      scope: "log.scope"
    };
  }

  // Subscribes to a real-time stream of log entries matching the specified scope.
  // The stream remains open until cancelled by the client or the connection is lost.
  // Permissions are re-validated periodically (every 1 second) to handle permission changes.
  //
  // For PersonalLogScope: Receives personal messages directed to the authenticated user.
  // Personal messages are transient and only delivered to active subscribers.
  //
  // Permissions required:
  // - GlobalLogScope: GLOBAL_SUBSCRIBE_LOGS
  // - InstanceLogScope/BotLogScope/InstanceScriptLogScope: INSTANCE_SUBSCRIBE_LOGS for the instance
  // - PersonalLogScope: No special permissions required
  //
  // Errors:
  // - PERMISSION_DENIED: User lacks required permission for the requested scope
  // - INVALID_ARGUMENT: Scope not set or invalid UUID format
  // - INTERNAL: Server error while setting up subscription
  rpc Subscribe (LogRequest) returns (stream LogResponse) {
    option (soulfire.v1.api_method) = {
      display_name: "Subscribe to logs"
      scope: "log.scope"
      execution: "Server streaming"
      side_effects: "Opens a live log stream until the client cancels the subscription."
    };
  }
}
