syntax = "proto3";

package api;

option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";

import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "internal/machine/api/pb/common.proto";

service Machine {
  // CheckPrerequisites verifies if the machine meets all necessary system requirements to participate in the cluster.
  rpc CheckPrerequisites(google.protobuf.Empty) returns (CheckPrerequisitesResponse);
  rpc InitCluster(InitClusterRequest) returns (InitClusterResponse);
  rpc JoinCluster(JoinClusterRequest) returns (google.protobuf.Empty);
  rpc Token(google.protobuf.Empty) returns (TokenResponse);
  // Deprecated: use InspectMachine instead.
  rpc Inspect(google.protobuf.Empty) returns (MachineInfo);
  // InspectMachine retrieves detailed information about the machine. Supports broadcasting to multiple machines.
  rpc InspectMachine(google.protobuf.Empty) returns (InspectMachineResponse);
  // InspectWireGuardNetwork retrieves the current WireGuard network configuration and peer status.
  rpc InspectWireGuardNetwork(google.protobuf.Empty) returns (InspectWireGuardNetworkResponse);
  // Reset restores the machine to a clean state, removing all cluster-related configuration and data.
  rpc Reset(ResetRequest) returns (google.protobuf.Empty);

  rpc InspectService(InspectServiceRequest) returns (InspectServiceResponse);

  rpc MachineLogs(LogsRequest) returns (stream LogEntry);
}

message MachineInfo {
  string id = 1;
  string name = 2;
  NetworkConfig network = 3;
  IP public_ip = 4;
}

message NetworkConfig {
  IPPrefix subnet = 1;
  IP management_ip = 2;
  repeated IPPort endpoints = 3;
  bytes public_key = 4;
}

message CheckPrerequisitesResponse {
  // Overall status of the checks.
  bool satisfied = 1;
  // Error message if not satisfied (empty if all checks pass).
  string error = 2;
}

message InitClusterRequest {
  string machineName = 1;
  IPPrefix network = 2;

  oneof public_ip_config {
    IP public_ip = 3;
    bool public_ip_auto = 4;
  }

  // Optional WireGuard endpoints other machines will use to connect to this machine instead of auto-discovered ones.
  repeated IPPort wireguard_endpoints = 5;
}

message InitClusterResponse {
  MachineInfo machine = 1;
}

message JoinClusterRequest {
  MachineInfo machine = 1;
  repeated MachineInfo other_machines = 3;
  // Minimum store database version the new machine should sync to before starting cluster operations.
  int64 min_store_db_version = 4;
}

message InspectMachineResponse {
  // Must contain only one repeated messages field to allow broadcasting InspectMachine requests to multiple machines.
  repeated MachineDetails machines = 1;
}

message MachineDetails {
  Metadata metadata = 1;
  MachineInfo machine = 2;
  // Current Corrosion cr-sqlite database version (Lamport timestamp) of the cluster store.
  int64 store_db_version = 3;
  // Round-trip times to other machines in the cluster, keyed by peer machine ID.
  map<string, RTTStats> rtts = 4;
}

message TokenResponse {
  string token = 1;
}

message ResetRequest {
}

message Service {
  string id = 1;
  string name = 2;
  string mode = 3;

  message Container {
    string machine_id = 1;
    // JSON encoded Docker types.Container.
    bytes container = 2;
  }

  repeated Container containers = 4;
}

message InspectServiceRequest {
  string id = 1;
}

message InspectServiceResponse {
  Service service = 1;
}

message InspectWireGuardNetworkResponse {
  string interface_name = 1;
  bytes public_key = 2;
  int32 listen_port = 3;
  repeated WireGuardPeer peers = 4;
}

message WireGuardPeer {
  bytes public_key = 1;
  string endpoint = 2;
  google.protobuf.Timestamp last_handshake_time = 3;
  int64 receive_bytes = 4;
  int64 transmit_bytes = 5;
  repeated string allowed_ips = 6;
}

message RTTStats {
  google.protobuf.Duration median = 1;
  google.protobuf.Duration std_dev = 2;
}
