syntax = "proto3";

package chatto.api.v1;

import "buf/validate/validate.proto";
import "chatto/api/v1/link_previews.proto";
import "chatto/api/v1/message_types.proto";
import "chatto/api/v1/reactions.proto";

// Request to create a message in a room or thread.
message CreateMessageRequest {
  // Field 8 was `link_preview`, a client-provided metadata message used before
  // CreateMessage accepted only server-issued link preview tokens.
  reserved 8;
  reserved "link_preview";
  // Field 9 was `attachments`, inline attachment bytes accepted before
  // AssetUploadService moved uploads into a dedicated chunked API.
  reserved 9;
  reserved "attachments";
  // Field 7 was `mention_confirmation_token`, used by a server-enforced large
  // mention confirmation flow before role mention confirmation became a
  // client-only composer affordance.
  reserved 7;
  reserved "mention_confirmation_token";

  // Required. Room where the message should be created.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Message body text. Required unless attachment_asset_ids is non-empty.
  string body = 2;
  // Existing room-scoped attachment asset IDs to include with the message.
  repeated string attachment_asset_ids = 3;
  // Event ID of the thread root message when posting a thread reply.
  string thread_root_event_id = 4;
  // Event ID this message replies to for attribution.
  string in_reply_to = 5;
  // True to also echo a thread reply into the main room timeline.
  bool also_send_to_channel = 6;
  // Short-lived token returned by FetchLinkPreview for the selected URL. The
  // server resolves the token to cached, server-fetched metadata during post.
  string link_preview_token = 10;
}

// Result of creating a message.
message CreateMessageResponse {
  // Field 2 was `mention_confirmation`, used by a server-enforced large
  // mention confirmation flow before role mention confirmation became a
  // client-only composer affordance.
  reserved 2;
  reserved "mention_confirmation";
  // Field 3 was `includes`, used before create responses were narrowed to the
  // generated message.
  reserved 3;
  reserved "includes";

  // Renderable message created by the request.
  Message message = 1;
}

// Request to patch a message.
message UpdateMessageRequest {
  // Required. Room containing the message.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Required. Event ID of the message to edit.
  string event_id = 2 [(buf.validate.field).string.min_len = 1];
  // New message body text. Omit to preserve the current body.
  optional string body = 3 [(buf.validate.field).string.max_len = 10000];
  // For thread replies, whether a channel echo should exist after saving.
  // Omit to preserve the current echo state.
  optional bool also_send_to_channel = 4;
}

// Result of editing a message.
message UpdateMessageResponse {
  reserved 1;
  reserved "updated";

  // Field 3 was `includes`, used before update responses were narrowed to the
  // edited message.
  reserved 3;
  reserved "includes";

  // Renderable message after the edit.
  Message message = 2;
}

// Request to retract a message.
message DeleteMessageRequest {
  // Required. Room containing the message.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Required. Event ID of the message to retract.
  string event_id = 2 [(buf.validate.field).string.min_len = 1];
}

// Result of retracting a message.
message DeleteMessageResponse {
  // True when the delete/retract request was accepted.
  bool deleted = 1;
}

// Request to remove one attachment from a message.
message DeleteAttachmentRequest {
  // Required. Room containing the message.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Required. Event ID of the message containing the attachment.
  string event_id = 2 [(buf.validate.field).string.min_len = 1];
  // Required. Attachment ID to remove from the message.
  string attachment_id = 3 [(buf.validate.field).string.min_len = 1];
}

// Result of removing one attachment from a message.
message DeleteAttachmentResponse {
  // True when the attachment removal was accepted.
  bool deleted = 1;
}

// Request to remove the accepted link preview from a message.
message DeleteLinkPreviewRequest {
  // Required. Room containing the message.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Required. Event ID of the message containing the link preview.
  string event_id = 2 [(buf.validate.field).string.min_len = 1];
  // Required. URL of the link preview to remove.
  string url = 3 [(buf.validate.field).string.min_len = 1];
}

// Result of removing a link preview from a message.
message DeleteLinkPreviewResponse {
  // True when the link preview removal was accepted.
  bool deleted = 1;
}

// Request to read one visible message.
message GetMessageRequest {
  // Field 3 was `thumbnail`, used before custom attachment URL hydration moved
  // to AssetService.
  reserved 3;
  reserved "thumbnail";

  // Required. Room containing the message.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Required. Message event ID.
  string event_id = 2 [(buf.validate.field).string.min_len = 1];
}

// Response containing one renderable message.
message GetMessageResponse {
  // Field 2 was `includes`, used before message read responses were narrowed
  // to messages.
  reserved 2;
  reserved "includes";

  // Renderable message.
  Message message = 1;
}

// Request to read many visible messages in one room.
message BatchGetMessagesRequest {
  // Field 3 was `thumbnail`, used before custom attachment URL hydration moved
  // to AssetService.
  reserved 3;
  reserved "thumbnail";

  // Required. Room containing the messages.
  string room_id = 1 [(buf.validate.field).string.min_len = 1];
  // Required. Message event IDs. Missing, retracted, non-message, and
  // wrong-room event IDs are omitted from the response.
  repeated string event_ids = 2 [(buf.validate.field).repeated = {
    min_items: 1
    max_items: 100
    items: {
      string: {min_len: 1}
    }
  }];
}

// Response containing renderable messages.
message BatchGetMessagesResponse {
  // Field 2 was `includes`, used before message read responses were narrowed
  // to messages.
  reserved 2;
  reserved "includes";

  // Renderable messages in first-seen request order.
  repeated Message messages = 1;
}

// Creates messages in room and thread timelines.
service MessageService {
  // Fetches and caches metadata for a composer URL. Authentication is required
  // to avoid exposing the preview fetcher as an unauthenticated network proxy.
  // Successful responses include a short-lived token accepted by CreateMessage.
  rpc FetchLinkPreview(FetchLinkPreviewRequest) returns (FetchLinkPreviewResponse);
  // Creates a message for the current user. The user must be a room member and
  // must have message.post for room messages or message.post-in-thread for
  // thread replies. Echoing a thread reply also requires message.echo and
  // message.post.
  rpc CreateMessage(CreateMessageRequest) returns (CreateMessageResponse);
  // Edits a message body. Authors can edit their own messages within the edit
  // window. Non-authors need message.manage and cannot change channel echo
  // state.
  rpc UpdateMessage(UpdateMessageRequest) returns (UpdateMessageResponse);
  // Retracts a message. Authors can delete their own messages. Non-authors need
  // message.manage.
  rpc DeleteMessage(DeleteMessageRequest) returns (DeleteMessageResponse);
  // Removes one attachment from the author's own message.
  rpc DeleteAttachment(DeleteAttachmentRequest) returns (DeleteAttachmentResponse);
  // Removes the accepted link preview from the author's own message.
  rpc DeleteLinkPreview(DeleteLinkPreviewRequest) returns (DeleteLinkPreviewResponse);
  // Reads one renderable message, including current body, attachment metadata,
  // link preview, reactions, and thread metadata. Authentication and room
  // membership are required. Returns NOT_FOUND when the event does not exist,
  // is not a message, has been retracted, or belongs to a different room.
  rpc GetMessage(GetMessageRequest) returns (GetMessageResponse);
  // Reads many renderable messages in one room. Authentication and room
  // membership are required. Missing, retracted, non-message, and wrong-room
  // event IDs are omitted. Results preserve first-seen request order and
  // repeated event IDs are de-duplicated.
  rpc BatchGetMessages(BatchGetMessagesRequest) returns (BatchGetMessagesResponse);
  // Adds a reaction to a message. The user must be a room member and have
  // message.react in the target room.
  rpc AddReaction(AddReactionRequest) returns (AddReactionResponse);
  // Removes a reaction from a message. The user must be a room member and have
  // message.react in the target room.
  rpc RemoveReaction(RemoveReactionRequest) returns (RemoveReactionResponse);
}
