@namespace("chat.1")
protocol remote {

  import idl "github.com/keybase/client/go/protocol/gregor1" as gregor1;

  record MessageBoxed {
    // This was not present in most V1 messages.
    MessageBoxedVersion version;

    // [V1, V2]: Only set when returned from the server; on the way up to the
    // server, they are null.
    union { null, MessageServerHeader } serverHeader;

    // [V1, V2]: MessageClientHeader is needed by clients to get keys via TLF name.
    // The server needs it as well for sender uid, device id.
    MessageClientHeader clientHeader;

    // V1: Encrypted HeaderPlaintext
    // V2: SignEncrypted HeaderPlaintext (using derived encryption key)
    SealedData headerCiphertext;

    // V1: Encrypted BodyPlaintext
    // V2: Encrypted BodyPlaintext (using derived key)
    EncryptedData bodyCiphertext;

    // V1: Missing
    // V2: KID of the signing key used on headerSealed.
    //     Used to open and verify headerSealed.
    //     Must be asserted to belong to the sender when unboxing.
    bytes verifyKey;

    // [V1, V2]: Key generation of the encryption key
    int keyGeneration;
  }

  enum MessageBoxedVersion {
    // 0 exists from before the version field. It means V1.
    VNONE_0,

    // V1: Encrypted header and body.
    // Hash of body inside header.
    // Sig of header inside header.
    V1_1,

    // V2: SignEncrypted header, encrypted body.
    // Hash of body inside header.
    V2_2
  }

  record ThreadViewBoxed {
    array<MessageBoxed> messages;
    union { null, Pagination } pagination;
  }

  record GetInboxRemoteRes {
    InboxView inbox;
    union { null, RateLimit } rateLimit;
  }

  record GetInboxByTLFIDRemoteRes {
    array<Conversation> convs;
    union { null, RateLimit } rateLimit;
  }

  record GetThreadRemoteRes {
    ThreadViewBoxed thread;
    union { null, RateLimit } rateLimit;
  }

  record GetConversationMetadataRemoteRes {
    Conversation conv;
    union { null, RateLimit } rateLimit;
  }

  record PostRemoteRes {
    MessageServerHeader msgHeader;
    union { null, RateLimit } rateLimit;
  }

  record NewConversationRemoteRes {
    ConversationID convID;
    union { null, RateLimit } rateLimit;
  }

  record GetMessagesRemoteRes {
    array<MessageBoxed> msgs;
    union { null, RateLimit } rateLimit;
  }

  record MarkAsReadRes {
    union { null, RateLimit } rateLimit;
  }

  record SetConversationStatusRes {
    union { null, RateLimit } rateLimit;
  }

  record GetPublicConversationsRes {
    array<Conversation> conversations; 
    union { null, RateLimit } rateLimit;
  }

  GetInboxRemoteRes getInboxRemote(InboxVers vers, union { null, GetInboxQuery } query, union { null, Pagination } pagination);
  GetThreadRemoteRes getThreadRemote(ConversationID conversationID, union { null, GetThreadQuery } query, union { null, Pagination } pagination);
  GetPublicConversationsRes getPublicConversations(TLFID tlfID, TopicType topicType, boolean summarizeMaxMsgs);

  PostRemoteRes postRemote(ConversationID conversationID, MessageBoxed messageBoxed);
  NewConversationRemoteRes newConversationRemote(ConversationIDTriple idTriple);

  // on duplication of idTriple, and error is returned and the conversation ID of the existing conversation is returned.
  @lint("ignore")
  NewConversationRemoteRes newConversationRemote2(ConversationIDTriple idTriple, MessageBoxed TLFMessage);
  GetMessagesRemoteRes getMessagesRemote(ConversationID conversationID, array<MessageID> messageIDs);

  MarkAsReadRes markAsRead(ConversationID conversationID, MessageID msgID);

  @lint("ignore")
  SetConversationStatusRes SetConversationStatus(ConversationID conversationID, ConversationStatus status);

  @lint("ignore")
  UnreadUpdateFull GetUnreadUpdateFull(InboxVers inboxVers);
  // The full set of updates of all convs in a single user's inbox
  record UnreadUpdateFull {
      // Set if this update should be ignored because the requester was already up to date
      boolean ignore;
      // The inbox version that this full update was derived from
      InboxVers inboxVers;
      array<UnreadUpdate> updates;
  }

  record S3Params {
    string bucket;
    string objectKey;
    string accessKey;
    string acl;
    string regionName;
    string regionEndpoint;
    string regionBucketEndpoint;
  }

  // getS3Params returns S3 params that the client needs to make S3
  // requests.
  S3Params getS3Params(ConversationID conversationID);

  // s3Sign signs a payload for S3 requests.
  bytes s3Sign(int version, bytes payload);

  // Get the inbox version for a user
  InboxVers getInboxVersion(gregor1.UID uid); 

  enum SyncInboxResType {
    CURRENT_0,
    INCREMENTAL_1,
    CLEAR_2
  }
  
  record SyncIncrementalRes {
    InboxVers vers;
    array<Conversation> convs;
  } 

  record ServerCacheVers {
    int inboxVers;
    int bodiesVers;
  }

  variant SyncInboxRes switch (SyncInboxResType typ) {
    case CURRENT: void;
    case INCREMENTAL: SyncIncrementalRes;
    case CLEAR: void;
  }

  // (DEPRECATED) Sync down the inbox given a current version
  SyncInboxRes syncInbox(InboxVers vers);

  record SyncChatRes {
    ServerCacheVers cacheVers;
    SyncInboxRes inboxRes;
  }

  SyncChatRes syncChat(InboxVers vers);

  enum SyncAllNotificationType {
    STATE_0,
    INCREMENTAL_1
  }

  variant SyncAllNotificationRes switch(SyncAllNotificationType typ) {
  case STATE: gregor1.State;
  case INCREMENTAL: gregor1.SyncResult;
  }

  record SyncAllResult {
    gregor1.AuthResult auth; 
    SyncChatRes chat;
    SyncAllNotificationRes notification;
    UnreadUpdateFull badge;
  }

  SyncAllResult syncAll(gregor1.UID uid, gregor1.DeviceID deviceID, gregor1.SessionToken session, InboxVers inboxVers, gregor1.Time ctime);

  // tlfFinalize is an endpoint for kbfstlfd to notify Gregor that a TLF ID has been finalized.
  // Gregor keeps an internal record of these TLF IDs, so that it can always return the latest
  // conversation per TLF ID on GetInboxRemote.
  void tlfFinalize(TLFID tlfID, string resetUser, string resetDate, gregor1.Time resetTimestamp, string resetFull);

  void tlfResolve(TLFID tlfID, array<gregor1.UID> resolvedWriters, array<gregor1.UID> resolvedReaders);

  // Pubsub endpoints
  void publishReadMessage(gregor1.UID uid, ConversationID convID, MessageID msgID);
  void publishSetConversationStatus(gregor1.UID uid, ConversationID convID, ConversationStatus status);
}
