syntax = "proto3";

package memos.api.v1;

import "api/v1/attachment_service.proto";
import "api/v1/common.proto";
import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";

option go_package = "gen/api/v1";

service MemoService {
  // CreateMemo creates a memo.
  rpc CreateMemo(CreateMemoRequest) returns (Memo) {
    option (google.api.http) = {
      post: "/api/v1/memos"
      body: "memo"
    };
    option (google.api.method_signature) = "memo";
  }
  // ListMemos lists memos with pagination and filter.
  rpc ListMemos(ListMemosRequest) returns (ListMemosResponse) {
    option (google.api.http) = {get: "/api/v1/memos"};
    option (google.api.method_signature) = "";
  }
  // GetMemo gets a memo.
  rpc GetMemo(GetMemoRequest) returns (Memo) {
    option (google.api.http) = {get: "/api/v1/{name=memos/*}"};
    option (google.api.method_signature) = "name";
  }
  // UpdateMemo updates a memo.
  rpc UpdateMemo(UpdateMemoRequest) returns (Memo) {
    option (google.api.http) = {
      patch: "/api/v1/{memo.name=memos/*}"
      body: "memo"
    };
    option (google.api.method_signature) = "memo,update_mask";
  }
  // DeleteMemo deletes a memo.
  rpc DeleteMemo(DeleteMemoRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/api/v1/{name=memos/*}"};
    option (google.api.method_signature) = "name";
  }
  // SetMemoAttachments sets attachments for a memo.
  rpc SetMemoAttachments(SetMemoAttachmentsRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      patch: "/api/v1/{name=memos/*}/attachments"
      body: "*"
    };
    option (google.api.method_signature) = "name";
  }
  // ListMemoAttachments lists attachments for a memo.
  rpc ListMemoAttachments(ListMemoAttachmentsRequest) returns (ListMemoAttachmentsResponse) {
    option (google.api.http) = {get: "/api/v1/{name=memos/*}/attachments"};
    option (google.api.method_signature) = "name";
  }
  // SetMemoRelations sets relations for a memo.
  rpc SetMemoRelations(SetMemoRelationsRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      patch: "/api/v1/{name=memos/*}/relations"
      body: "*"
    };
    option (google.api.method_signature) = "name";
  }
  // ListMemoRelations lists relations for a memo.
  rpc ListMemoRelations(ListMemoRelationsRequest) returns (ListMemoRelationsResponse) {
    option (google.api.http) = {get: "/api/v1/{name=memos/*}/relations"};
    option (google.api.method_signature) = "name";
  }
  // CreateMemoComment creates a comment for a memo.
  rpc CreateMemoComment(CreateMemoCommentRequest) returns (Memo) {
    option (google.api.http) = {
      post: "/api/v1/{name=memos/*}/comments"
      body: "comment"
    };
    option (google.api.method_signature) = "name,comment";
  }
  // ListMemoComments lists comments for a memo.
  rpc ListMemoComments(ListMemoCommentsRequest) returns (ListMemoCommentsResponse) {
    option (google.api.http) = {get: "/api/v1/{name=memos/*}/comments"};
    option (google.api.method_signature) = "name";
  }
  // ListMemoReactions lists reactions for a memo.
  rpc ListMemoReactions(ListMemoReactionsRequest) returns (ListMemoReactionsResponse) {
    option (google.api.http) = {get: "/api/v1/{name=memos/*}/reactions"};
    option (google.api.method_signature) = "name";
  }
  // UpsertMemoReaction upserts a reaction for a memo.
  rpc UpsertMemoReaction(UpsertMemoReactionRequest) returns (Reaction) {
    option (google.api.http) = {
      post: "/api/v1/{name=memos/*}/reactions"
      body: "*"
    };
    option (google.api.method_signature) = "name";
  }
  // DeleteMemoReaction deletes a reaction for a memo.
  rpc DeleteMemoReaction(DeleteMemoReactionRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/api/v1/{name=memos/*/reactions/*}"};
    option (google.api.method_signature) = "name";
  }
  // CreateMemoShare creates a share link for a memo. Requires authentication as the memo creator.
  rpc CreateMemoShare(CreateMemoShareRequest) returns (MemoShare) {
    option (google.api.http) = {
      post: "/api/v1/{parent=memos/*}/shares"
      body: "memo_share"
    };
    option (google.api.method_signature) = "parent,memo_share";
  }
  // ListMemoShares lists all share links for a memo. Requires authentication as the memo creator.
  rpc ListMemoShares(ListMemoSharesRequest) returns (ListMemoSharesResponse) {
    option (google.api.http) = {get: "/api/v1/{parent=memos/*}/shares"};
    option (google.api.method_signature) = "parent";
  }
  // DeleteMemoShare revokes a share link. Requires authentication as the memo creator.
  rpc DeleteMemoShare(DeleteMemoShareRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/api/v1/{name=memos/*/shares/*}"};
    option (google.api.method_signature) = "name";
  }
  // GetMemoByShare resolves a share token to its memo. No authentication required.
  // Returns NOT_FOUND if the token is invalid or expired.
  rpc GetMemoByShare(GetMemoByShareRequest) returns (Memo) {
    option (google.api.http) = {get: "/api/v1/shares/{share_id}"};
  }
}

enum Visibility {
  VISIBILITY_UNSPECIFIED = 0;
  PRIVATE = 1;
  PROTECTED = 2;
  PUBLIC = 3;
}

message Reaction {
  option (google.api.resource) = {
    type: "memos.api.v1/Reaction"
    pattern: "memos/{memo}/reactions/{reaction}"
    name_field: "name"
    singular: "reaction"
    plural: "reactions"
  };

  // The resource name of the reaction.
  // Format: memos/{memo}/reactions/{reaction}
  string name = 1 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.field_behavior) = IDENTIFIER
  ];

  // The resource name of the creator.
  // Format: users/{user}
  string creator = 2 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = {type: "memos.api.v1/User"}
  ];

  // The resource name of the content.
  // For memo reactions, this should be the memo's resource name.
  // Format: memos/{memo}
  string content_id = 3 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Required. The type of reaction (e.g., "👍", "❤️", "😄").
  string reaction_type = 4 [(google.api.field_behavior) = REQUIRED];

  // Output only. The creation timestamp.
  google.protobuf.Timestamp create_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message Memo {
  option (google.api.resource) = {
    type: "memos.api.v1/Memo"
    pattern: "memos/{memo}"
    name_field: "name"
    singular: "memo"
    plural: "memos"
  };

  // The resource name of the memo.
  // Format: memos/{memo}, memo is the user defined id or uuid.
  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  // The state of the memo.
  State state = 2 [(google.api.field_behavior) = REQUIRED];

  // The name of the creator.
  // Format: users/{user}
  string creator = 3 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = {type: "memos.api.v1/User"}
  ];

  // The creation timestamp.
  // If not set on creation, the server will set it to the current time.
  google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OPTIONAL];

  // The last update timestamp.
  // If not set on creation, the server will set it to the current time.
  google.protobuf.Timestamp update_time = 5 [(google.api.field_behavior) = OPTIONAL];

  // The display timestamp of the memo.
  google.protobuf.Timestamp display_time = 6 [(google.api.field_behavior) = OPTIONAL];

  // Required. The content of the memo in Markdown format.
  string content = 7 [(google.api.field_behavior) = REQUIRED];

  // The visibility of the memo.
  Visibility visibility = 9 [(google.api.field_behavior) = REQUIRED];

  // Output only. The tags extracted from the content.
  repeated string tags = 10 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Whether the memo is pinned.
  bool pinned = 11 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The attachments of the memo.
  repeated Attachment attachments = 12 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The relations of the memo.
  repeated MemoRelation relations = 13 [(google.api.field_behavior) = OPTIONAL];

  // Output only. The reactions to the memo.
  repeated Reaction reactions = 14 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The computed properties of the memo.
  Property property = 15 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The name of the parent memo.
  // Format: memos/{memo}
  optional string parent = 16 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Output only. The snippet of the memo content. Plain text only.
  string snippet = 17 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. The location of the memo.
  optional Location location = 18 [(google.api.field_behavior) = OPTIONAL];

  // Computed properties of a memo.
  message Property {
    bool has_link = 1;
    bool has_task_list = 2;
    bool has_code = 3;
    bool has_incomplete_tasks = 4;
    // The title extracted from the first H1 heading, if present.
    string title = 5;
  }
}

message Location {
  // A placeholder text for the location.
  string placeholder = 1 [(google.api.field_behavior) = OPTIONAL];

  // The latitude of the location.
  double latitude = 2 [(google.api.field_behavior) = OPTIONAL];

  // The longitude of the location.
  double longitude = 3 [(google.api.field_behavior) = OPTIONAL];
}

message CreateMemoRequest {
  // Required. The memo to create.
  Memo memo = 1 [(google.api.field_behavior) = REQUIRED];

  // Optional. The memo ID to use for this memo.
  // If empty, a unique ID will be generated.
  string memo_id = 2 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemosRequest {
  // Optional. The maximum number of memos to return.
  // The service may return fewer than this value.
  // If unspecified, at most 50 memos will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];

  // Optional. A page token, received from a previous `ListMemos` call.
  // Provide this to retrieve the subsequent page.
  string page_token = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The state of the memos to list.
  // Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
  State state = 3 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The order to sort results by.
  // Default to "display_time desc".
  // Supports comma-separated list of fields following AIP-132.
  // Example: "pinned desc, display_time desc" or "create_time asc"
  // Supported fields: pinned, display_time, create_time, update_time, name
  string order_by = 4 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Filter to apply to the list results.
  // Filter is a CEL expression to filter memos.
  // Refer to `Shortcut.filter`.
  string filter = 5 [(google.api.field_behavior) = OPTIONAL];

  // Optional. If true, show deleted memos in the response.
  bool show_deleted = 6 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemosResponse {
  // The list of memos.
  repeated Memo memos = 1;

  // A token that can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

message GetMemoRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];
}

message UpdateMemoRequest {
  // Required. The memo to update.
  // The `name` field is required.
  Memo memo = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. The list of fields to update.
  google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED];
}

message DeleteMemoRequest {
  // Required. The resource name of the memo to delete.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Optional. If set to true, the memo will be deleted even if it has associated data.
  bool force = 2 [(google.api.field_behavior) = OPTIONAL];
}

message SetMemoAttachmentsRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Required. The attachments to set for the memo.
  repeated Attachment attachments = 2 [(google.api.field_behavior) = REQUIRED];
}

message ListMemoAttachmentsRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Optional. The maximum number of attachments to return.
  int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. A page token for pagination.
  string page_token = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemoAttachmentsResponse {
  // The list of attachments.
  repeated Attachment attachments = 1;

  // A token for the next page of results.
  string next_page_token = 2;
}

message MemoRelation {
  // The memo in the relation.
  Memo memo = 1 [(google.api.field_behavior) = REQUIRED];

  // The related memo.
  Memo related_memo = 2 [(google.api.field_behavior) = REQUIRED];

  // The type of the relation.
  enum Type {
    TYPE_UNSPECIFIED = 0;
    REFERENCE = 1;
    COMMENT = 2;
  }
  Type type = 3 [(google.api.field_behavior) = REQUIRED];

  // Memo reference in relations.
  message Memo {
    // The resource name of the memo.
    // Format: memos/{memo}
    string name = 1 [
      (google.api.field_behavior) = REQUIRED,
      (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
    ];

    // Output only. The snippet of the memo content. Plain text only.
    string snippet = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  }
}

message SetMemoRelationsRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Required. The relations to set for the memo.
  repeated MemoRelation relations = 2 [(google.api.field_behavior) = REQUIRED];
}

message ListMemoRelationsRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Optional. The maximum number of relations to return.
  int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. A page token for pagination.
  string page_token = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemoRelationsResponse {
  // The list of relations.
  repeated MemoRelation relations = 1;

  // A token for the next page of results.
  string next_page_token = 2;
}

message CreateMemoCommentRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Required. The comment to create.
  Memo comment = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The comment ID to use.
  string comment_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemoCommentsRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Optional. The maximum number of comments to return.
  int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. A page token for pagination.
  string page_token = 3 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The order to sort results by.
  string order_by = 4 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemoCommentsResponse {
  // The list of comment memos.
  repeated Memo memos = 1;

  // A token for the next page of results.
  string next_page_token = 2;

  // The total count of comments.
  int32 total_size = 3;
}

message ListMemoReactionsRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Optional. The maximum number of reactions to return.
  int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. A page token for pagination.
  string page_token = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ListMemoReactionsResponse {
  // The list of reactions.
  repeated Reaction reactions = 1;

  // A token for the next page of results.
  string next_page_token = 2;

  // The total count of reactions.
  int32 total_size = 3;
}

message UpsertMemoReactionRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Required. The reaction to upsert.
  Reaction reaction = 2 [(google.api.field_behavior) = REQUIRED];
}

message DeleteMemoReactionRequest {
  // Required. The resource name of the reaction to delete.
  // Format: memos/{memo}/reactions/{reaction}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Reaction"}
  ];
}

// MemoShare is an access grant that permits read-only access to a memo via an opaque bearer token.
message MemoShare {
  option (google.api.resource) = {
    type: "memos.api.v1/MemoShare"
    pattern: "memos/{memo}/shares/{share}"
    singular: "share"
    plural: "shares"
  };

  // The resource name of the share. Format: memos/{memo}/shares/{share}
  // The {share} segment is the opaque token used in the share URL.
  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  // Output only. When this share link was created.
  google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. When set, the share link stops working after this time.
  // If unset, the link never expires.
  optional google.protobuf.Timestamp expire_time = 3 [(google.api.field_behavior) = OPTIONAL];
}

message CreateMemoShareRequest {
  // Required. The resource name of the memo to share.
  // Format: memos/{memo}
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];

  // Required. The share to create.
  MemoShare memo_share = 2 [(google.api.field_behavior) = REQUIRED];
}

message ListMemoSharesRequest {
  // Required. The resource name of the memo.
  // Format: memos/{memo}
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
  ];
}

message ListMemoSharesResponse {
  // The list of share links.
  repeated MemoShare memo_shares = 1;
}

message DeleteMemoShareRequest {
  // Required. The resource name of the share to delete.
  // Format: memos/{memo}/shares/{share}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type: "memos.api.v1/MemoShare"}
  ];
}

message GetMemoByShareRequest {
  // Required. The share token extracted from the share URL (/s/{share_id}).
  string share_id = 1 [(google.api.field_behavior) = REQUIRED];
}
