syntax = "proto3";

package chatto.api.v1;

import "buf/validate/validate.proto";
import "chatto/api/v1/rooms.proto";
import "chatto/api/v1/users.proto";
import "google/protobuf/timestamp.proto";

// Voice-call request/response APIs backed by Chatto call-state projections.
service VoiceCallService {
  // Lists member channel rooms that currently have active calls as a finite
  // runtime snapshot. Rooms the caller is not a member of are omitted.
  //
  // Returns an empty list when LiveKit is not configured.
  rpc ListActiveCalls(ListActiveCallsRequest) returns (ListActiveCallsResponse);

  // Gets the current active call snapshot for one room.
  //
  // The caller must be a member of the room. Returns NOT_FOUND when the room
  // does not exist, currently has no active call, or voice calls are not
  // configured. The returned call_id lets clients ignore stale realtime call
  // events from previous calls in the same room.
  rpc GetActiveCall(GetActiveCallRequest) returns (GetActiveCallResponse);

  // Gets current active call snapshots for many rooms.
  //
  // Rooms that do not exist, are inaccessible, or currently have no active call
  // are omitted. Results preserve first-seen request order and repeated room
  // IDs are de-duplicated. Returns an empty list when LiveKit is not configured.
  rpc BatchGetActiveCalls(BatchGetActiveCallsRequest) returns (BatchGetActiveCallsResponse);

  // Lists participants currently observed in a room's active call as a finite
  // runtime snapshot.
  //
  // The caller must be a member of the room.
  // Returns NOT_FOUND when the room does not exist and PERMISSION_DENIED when
  // the caller is not a room member.
  rpc ListCallParticipants(ListCallParticipantsRequest) returns (ListCallParticipantsResponse);

  // Records the caller's intent to join a room call.
  //
  // Returns joined=false when LiveKit is not configured.
  rpc JoinCall(JoinCallRequest) returns (JoinCallResponse);

  // Issues a LiveKit token for joining the room's active call.
  //
  // The caller must be a member of the room and a call must already be active.
  // Returns NOT_FOUND when the room does not exist, PERMISSION_DENIED when the
  // caller is not a room member, and FAILED_PRECONDITION when no call is active
  // or voice and video calls are not configured.
  rpc GetCallToken(GetCallTokenRequest) returns (GetCallTokenResponse);

  // Records the caller's intent to leave a room call.
  //
  // Returns left=false when LiveKit is not configured.
  rpc LeaveCall(LeaveCallRequest) returns (LeaveCallResponse);
}

// Request for active channel call snapshots.
message ListActiveCallsRequest {}

// Finite runtime snapshot of active channel calls.
message ListActiveCallsResponse {
  // Active calls in room order returned by the call-state projection.
  repeated ActiveCall calls = 1;
}

// Current active call snapshot for one room.
message ActiveCall {
  reserved 1;
  reserved "room_id";

  // Room containing the active call.
  RoomSummary room = 4;
  // Active call session ID.
  string call_id = 2;
  // Participants currently projected for this call.
  repeated CallParticipant participants = 3;
}

// Request for one room's active call snapshot.
message GetActiveCallRequest {
  // Required. Room whose active call should be inspected.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
}

// Response for one room's active call snapshot.
message GetActiveCallResponse {
  // Current active call.
  ActiveCall call = 1;
}

// Request active call snapshots for many rooms.
message BatchGetActiveCallsRequest {
  // Required room IDs. Unknown, inaccessible, and inactive rooms are omitted.
  repeated string room_ids = 1 [(buf.validate.field).repeated = {
    min_items: 1
    max_items: 100
    items: {
      string: {min_len: 1}
    }
  }];
}

// Active call batch response.
message BatchGetActiveCallsResponse {
  // Active calls in first-seen request order.
  repeated ActiveCall calls = 1;
}

// Request for participants in a room call.
message ListCallParticipantsRequest {
  // Required. Room whose active call should be inspected.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
}

// Finite runtime snapshot of participants in one room call.
message ListCallParticipantsResponse {
  // Participants currently projected for the room call.
  repeated CallParticipant participants = 1;
}

// User currently participating in a room call.
message CallParticipant {
  // Participant user.
  User user = 1;
  // When the user joined this call.
  google.protobuf.Timestamp joined_at = 2;
  // Active call session ID.
  string call_id = 3;
}

// Request to record joining a room call.
message JoinCallRequest {
  // Required. Room whose call is being joined.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
}

// Response from recording a join intent.
message JoinCallResponse {
  // True when a join fact was recorded.
  bool joined = 1;
}

// Request for a LiveKit token for a room call.
message GetCallTokenRequest {
  // Required. Room whose active call should be joined.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
}

// LiveKit token details for joining a room call.
message GetCallTokenResponse {
  // LiveKit JWT token.
  string token = 1;
  // Shared E2EE key for this active call.
  string e2ee_key = 2;
  // Active call session ID.
  string call_id = 3;
}

// Request to record leaving a room call.
message LeaveCallRequest {
  // Required. Room whose call is being left.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
}

// Response from recording a leave intent.
message LeaveCallResponse {
  // True when a leave fact was recorded.
  bool left = 1;
}
