syntax = "proto3";

package photon.imessage.v1;

import "photon/imessage/v1/attachment_types.proto";

option swift_prefix = "PIMsg_";


// ---------------------------------------------------------------------------
// Service
// ---------------------------------------------------------------------------

// Attachment metadata reads, uploads, and downloads. All transfers are
// end-to-end on this gRPC channel; no external blob store is involved.
service AttachmentService {

  // Metadata-only lookup. Cheap; never touches attachment bytes.
  rpc GetAttachmentInfo(GetAttachmentInfoRequest) returns (GetAttachmentInfoResponse);

  // Atomic upload of one primary file plus an optional sidecar. The
  // server persists both before responding; on failure neither is visible
  // to subsequent reads.
  rpc UploadAttachment(UploadAttachmentRequest) returns (UploadAttachmentResponse);

  // Server-streaming bytes. See `DownloadAttachmentResponse` for the
  // frame contract.
  rpc DownloadAttachment(DownloadAttachmentRequest) returns (stream DownloadAttachmentResponse);

}


// ---------------------------------------------------------------------------
// Metadata
// ---------------------------------------------------------------------------

message GetAttachmentInfoRequest {

  string attachment_guid = 1;

}


message GetAttachmentInfoResponse {

  AttachmentInfo attachment = 1;

}


// ---------------------------------------------------------------------------
// Upload
// ---------------------------------------------------------------------------

message UploadAttachmentRequest {

  // Plain file name. Server normalizes: strips path components, replaces
  // path separators (`/`, `\`) with `_`, drops control characters,
  // truncates to a fixed UTF-8 byte budget while preserving the
  // extension, and falls back to `attachment` if the result is empty.
  string file_name = 1;

  bytes data = 2;

  optional Companion companion = 3;

}


message UploadAttachmentResponse {

  AttachmentInfo attachment = 1;

  // Set iff the request carried a companion.
  optional CompanionInfo companion = 2;

}


// ---------------------------------------------------------------------------
// Download
// ---------------------------------------------------------------------------

message DownloadAttachmentRequest {

  string attachment_guid = 1;

}


message DownloadHeader {

  AttachmentInfo attachment = 1;

  optional CompanionInfo companion = 2;

}


// Server-stream frame contract:
//
//   Frame 0       exactly one `header`
//   Frames 1..N   `primary_chunk*`
//   Frames N+1..M `companion_chunk*` emitted only when a companion exists
//   Close         gRPC half-close, no sentinel frame
message DownloadAttachmentResponse {

  oneof payload {

    DownloadHeader header = 1;

    bytes primary_chunk = 2;

    bytes companion_chunk = 3;

  }

}
