syntax = "proto3";

package hyapp.room.v1;

option go_package = "hyapp.local/api/proto/room/v1;roomv1";

// RequestMeta 固定承载所有命令的调用元信息。
// room-service 用它串起幂等、追踪、路由和业务操作者身份。
message RequestMeta {
  string request_id = 1;
  string command_id = 2;
  int64 actor_user_id = 3;
  // room_id 是命令路由、持久化和腾讯 IM/RTC 映射共同依赖的必填字段。
  // CreateRoom 时必须满足 ^[A-Za-z0-9_-]{1,48}$。
  string room_id = 4;
  string gateway_node_id = 5;
  string session_id = 6;
  int64 sent_at_ms = 7;
  string app_code = 8;
}

// CommandResult 统一返回命令是否真正落地，以及落地后的房间版本。
message CommandResult {
  bool applied = 1;
  int64 room_version = 2;
  int64 server_time_ms = 3;
}

// RoomUser 只表达 room-service 需要保存的房间内轻量用户态。
message RoomUser {
  int64 user_id = 1;
  string role = 2;
  int64 joined_at_ms = 3;
  int64 last_seen_at_ms = 4;
}

// SeatState 表达单个麦位当前占用和 RTC 发流确认状态。
message SeatState {
  int32 seat_no = 1;
  int64 user_id = 2;
  bool locked = 3;
  // publish_state 为空表示空麦或未进入发流流程；上麦后先进入 pending_publish，确认后进入 publishing。
  string publish_state = 4;
  // mic_session_id 是单次上麦发流会话 ID；所有客户端确认或 RTC webhook 必须带回它。
  string mic_session_id = 5;
  // publish_deadline_ms 是 pending_publish 必须确认发流的截止时间。
  int64 publish_deadline_ms = 6;
  // mic_session_room_version 是创建本次 mic_session 的房间版本，用于丢弃旧版本 RTC 事件。
  int64 mic_session_room_version = 7;
  // last_publish_event_time_ms 记录已接受的最新 RTC/客户端发流事件时间。
  int64 last_publish_event_time_ms = 8;
  // mic_muted 是服务端可见的麦克风静音状态，用于同步其他用户 UI。
  bool mic_muted = 9;
}

// RankItem 表达房间内本地礼物榜项目。
message RankItem {
  int64 user_id = 1;
  int64 score = 2;
  int64 gift_value = 3;
  int64 updated_at_ms = 4;
}

// RoomSnapshot 把 room-service 对外需要暴露的房间投影集中返回。
message RoomSnapshot {
  string room_id = 1;
  int64 owner_user_id = 2;
  string mode = 4;
  string status = 5;
  bool chat_enabled = 6;
  repeated SeatState mic_seats = 7;
  repeated RoomUser online_users = 8;
  repeated int64 admin_user_ids = 9;
  repeated int64 ban_user_ids = 10;
  repeated int64 mute_user_ids = 11;
  repeated RankItem gift_rank = 12;
  map<string, string> room_ext = 13;
  int64 heat = 14;
  int64 version = 15;
  string app_code = 16;
  string room_short_id = 17;
  int64 visible_region_id = 18;
}

// CreateRoomRequest 创建一个新的房间执行单元。
// 必填语义：meta.room_id、meta.command_id、meta.actor_user_id、mode 和 room_name。
// 同一个 meta.actor_user_id 只能作为 owner 创建一个房间；重复创建返回 Conflict。
message CreateRoomRequest {
  RequestMeta meta = 1;
  // seat_count 为空或 0 时使用后台房间配置默认值；正数必须命中后台启用座位数。
  int32 seat_count = 2;
  // mode 必须非空；当前只作为房间状态字段保存和透出。
  string mode = 3;
  // visible_region_id 由 gateway 按创建者 user-service region_id 填充；0 表示 GLOBAL 兜底列表桶。
  int64 visible_region_id = 4;
  // room_name 是客户端创建房间时填写的展示名称，room-service 会写入 RoomSnapshot.room_ext["title"]。
  string room_name = 5;
  // room_avatar 是客户端可选上传的展示头像；为空时 room-service 写入默认系统头像。
  string room_avatar = 6;
  // room_description 是房间简介，只作为房间扩展资料保存，不参与 Room Cell 高频状态决策。
  string room_description = 7;
  // room_short_id 是房间短 ID，首版直接等于创建者当前 default/current display_user_id。
  string room_short_id = 8;
}

// CreateRoomResponse 返回新建后的房间快照。
message CreateRoomResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// UpdateRoomProfileRequest 修改房间展示资料和麦位数量。
// 该命令只允许当前在房间 presence 内的 owner 执行，座位数必须命中后台启用配置。
message UpdateRoomProfileRequest {
  RequestMeta meta = 1;
  optional string room_name = 2;
  optional string room_avatar = 3;
  optional string room_description = 4;
  optional int32 seat_count = 5;
}

// UpdateRoomProfileResponse 返回修改后的最新房间快照。
message UpdateRoomProfileResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// JoinRoomRequest 把用户 presence 接入房间业务态。
message JoinRoomRequest {
  RequestMeta meta = 1;
  string role = 2;
}

// JoinRoomResponse 返回加入后的房间快照。
message JoinRoomResponse {
  CommandResult result = 1;
  RoomUser user = 2;
  RoomSnapshot room = 3;
}

// RoomHeartbeatRequest 显式刷新已经存在的房间业务 presence。
// 它不能替代 JoinRoom；用户不在房间时必须拒绝，避免心跳把已离开的用户重新加入房间。
message RoomHeartbeatRequest {
  RequestMeta meta = 1;
}

// RoomHeartbeatResponse 返回刷新后的用户 presence 和房间快照。
message RoomHeartbeatResponse {
  CommandResult result = 1;
  RoomUser user = 2;
  RoomSnapshot room = 3;
}

// LeaveRoomRequest 把用户从房间 presence 移出。
message LeaveRoomRequest {
  RequestMeta meta = 1;
}

// LeaveRoomResponse 返回离房后的房间快照。
message LeaveRoomResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// CloseRoomRequest 关闭房间并清理业务 presence 和麦位。
message CloseRoomRequest {
  RequestMeta meta = 1;
  string reason = 2;
}

// CloseRoomResponse 返回关闭后的房间快照。
message CloseRoomResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// MicUpRequest 申请把用户放到指定麦位。
message MicUpRequest {
  RequestMeta meta = 1;
  int32 seat_no = 2;
}

// MicUpResponse 返回最新房间快照与目标麦位。
message MicUpResponse {
  CommandResult result = 1;
  int32 seat_no = 2;
  RoomSnapshot room = 3;
  string mic_session_id = 4;
  int64 publish_deadline_ms = 5;
}

// MicDownRequest 把用户从当前麦位移下。
message MicDownRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  // reason 为空表示主动下麦；系统自动释放麦位时会写入稳定原因，例如 publish_timeout。
  string reason = 3;
}

// MicDownResponse 返回最新房间快照与目标麦位。
message MicDownResponse {
  CommandResult result = 1;
  int32 seat_no = 2;
  RoomSnapshot room = 3;
}

// ChangeMicSeatRequest 直接调整指定用户所在麦位。
message ChangeMicSeatRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  int32 seat_no = 3;
}

// ChangeMicSeatResponse 返回变更后的房间快照。
message ChangeMicSeatResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// ConfirmMicPublishingRequest 确认当前 mic_session 已经在 RTC 侧成功发布音频。
// 客户端 SDK 回调和后续 RTC webhook 都必须带 mic_session_id、room_version 和 event_time_ms。
message ConfirmMicPublishingRequest {
  RequestMeta meta = 1;
  // target_user_id 为空时默认确认 actor_user_id；RTC webhook 入口可显式指定目标用户。
  int64 target_user_id = 2;
  string mic_session_id = 3;
  int64 room_version = 4;
  int64 event_time_ms = 5;
  string source = 6;
}

// ConfirmMicPublishingResponse 返回确认后的最新房间快照。
message ConfirmMicPublishingResponse {
  CommandResult result = 1;
  int32 seat_no = 2;
  RoomSnapshot room = 3;
}

// SetMicMuteRequest 修改麦位上的服务端可见静音态。
message SetMicMuteRequest {
  RequestMeta meta = 1;
  // target_user_id 为空时默认修改 actor_user_id 自己的麦克风静音态。
  int64 target_user_id = 2;
  bool muted = 3;
}

// SetMicMuteResponse 返回静音态变更后的最新房间快照。
message SetMicMuteResponse {
  CommandResult result = 1;
  int32 seat_no = 2;
  RoomSnapshot room = 3;
}

// ApplyRTCEventRequest 把可信 RTC 服务端回调转换成 Room Cell 命令。
// event_type 当前只接受 audio_started、audio_stopped、room_exited。
message ApplyRTCEventRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  string event_type = 3;
  int64 event_time_ms = 4;
  string reason = 5;
  string source = 6;
  string external_event_id = 7;
}

// ApplyRTCEventResponse 返回 RTC 事件落房间状态后的快照。
message ApplyRTCEventResponse {
  CommandResult result = 1;
  int32 seat_no = 2;
  RoomSnapshot room = 3;
}

// SetMicSeatLockRequest 锁定或解锁指定麦位。
message SetMicSeatLockRequest {
  RequestMeta meta = 1;
  int32 seat_no = 2;
  bool locked = 3;
}

// SetMicSeatLockResponse 返回锁麦后的最新房间快照。
message SetMicSeatLockResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// SetChatEnabledRequest 开启或关闭房间公屏。
message SetChatEnabledRequest {
  RequestMeta meta = 1;
  bool enabled = 2;
}

// SetChatEnabledResponse 返回聊天开关变更后的最新房间快照。
message SetChatEnabledResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// SetRoomAdminRequest 添加或移除房间管理员。
message SetRoomAdminRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  bool enabled = 3;
}

// SetRoomAdminResponse 返回管理员集合变更后的最新房间快照。
message SetRoomAdminResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// MuteUserRequest 修改房间内禁言态。
message MuteUserRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  bool muted = 3;
}

// MuteUserResponse 返回最新房间快照。
message MuteUserResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// KickUserRequest 把指定用户踢出房间。
message KickUserRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
}

// KickUserResponse 返回最新房间快照。
message KickUserResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// UnbanUserRequest 解除房间内 ban，用户仍需重新 JoinRoom。
message UnbanUserRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
}

// UnbanUserResponse 返回解封后的最新房间快照。
message UnbanUserResponse {
  CommandResult result = 1;
  RoomSnapshot room = 2;
}

// SystemEvictUserRequest 是平台级封禁、风控或后台治理触发的系统驱逐入口。
// 该命令仍由 Room Cell 修改房间状态；调用方不能直接写房间 presence 或麦位。
message SystemEvictUserRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  int64 operator_user_id = 3;
  string reason = 4;
  // ban_from_room 为 true 时把用户加入当前房间 ban 集合，防止同一房间恢复入口重新进入。
  bool ban_from_room = 5;
}

// SystemEvictUserResponse 返回系统驱逐的房间状态和外部 RTC 副作用结果。
message SystemEvictUserResponse {
  bool had_current_room = 1;
  CommandResult result = 2;
  RoomSnapshot room = 3;
  string room_id = 4;
  bool rtc_kicked = 5;
  string rtc_kick_error = 6;
}

// SendGiftRequest 是首版房间内最重要的变现命令。
message SendGiftRequest {
  RequestMeta meta = 1;
  // target_user_id 是兼容旧客户端的单目标字段；新客户端优先使用 target_user_ids。
  int64 target_user_id = 2;
  string gift_id = 3;
  int32 gift_count = 4;
  // target_type 当前只提交 user；后续扩展 all_mic、all_room、couple 时不再改 HTTP 契约。
  string target_type = 5;
  repeated int64 target_user_ids = 6;
}

// SendGiftResponse 返回扣费成功并落到房间后的状态结果。
message SendGiftResponse {
  CommandResult result = 1;
  string billing_receipt_id = 2;
  int64 room_heat = 3;
  repeated RankItem gift_rank = 4;
  RoomSnapshot room = 5;
}

// CheckSpeakPermissionRequest 让腾讯云 IM 发言回调或 gateway 在公屏前同步问房间业务态。
message CheckSpeakPermissionRequest {
  string room_id = 1;
  int64 user_id = 2;
  string app_code = 3;
}

// CheckSpeakPermissionResponse 返回当前用户是否允许发言。
message CheckSpeakPermissionResponse {
  bool allowed = 1;
  string reason = 2;
  int64 room_version = 3;
}

// VerifyRoomPresenceRequest 让外部 IM 入口在进群前确认用户业务上仍在房间内。
message VerifyRoomPresenceRequest {
  string room_id = 1;
  int64 user_id = 2;
  string session_id = 3;
  string request_id = 4;
  string app_code = 5;
}

// VerifyRoomPresenceResponse 返回用户是否仍然属于该房间。
message VerifyRoomPresenceResponse {
  bool present = 1;
  string reason = 2;
  int64 room_version = 3;
}

// ListRoomsRequest 查询当前用户所在区域可见的房间列表。
// visible_region_id 必须来自 gateway 对 user-service 的 GetUser 结果，客户端不能直接提交。
message ListRoomsRequest {
  RequestMeta meta = 1;
  int64 viewer_user_id = 2;
  int64 visible_region_id = 3;
  string tab = 4;
  string cursor = 5;
  int32 limit = 6;
  string query = 7;
}

// ListRoomFeedsRequest 查询 Mine 页 visited/friend/following 用户房间流。
// 它和公共房间发现列表分离，避免把用户关系流误建模成房间全集过滤。
message ListRoomFeedsRequest {
  RequestMeta meta = 1;
  int64 viewer_user_id = 2;
  int64 visible_region_id = 3;
  string tab = 4;
  string cursor = 5;
  int32 limit = 6;
  string query = 7;
  // related_users 只用于 friend/following；关系事实由 gateway 从 user-service 读取后传入。
  repeated RoomFeedRelatedUser related_users = 8;
}

// RoomFeedRelatedUser 是 Mine 关系房间流的排序输入，不让 room-service 持有好友/关注事实。
message RoomFeedRelatedUser {
  int64 user_id = 1;
  int64 relation_updated_at_ms = 2;
}

// RoomListItem 是房间发现页卡片读模型，不承载完整 Room Cell 状态。
message RoomListItem {
  string room_id = 1;
  int64 owner_user_id = 2;
  string title = 4;
  string cover_url = 5;
  string mode = 6;
  string status = 7;
  int64 heat = 8;
  int32 online_count = 9;
  int32 seat_count = 10;
  int32 occupied_seat_count = 11;
  int64 visible_region_id = 12;
  string app_code = 13;
  string room_short_id = 14;
}

// ListRoomsResponse 返回一页房间卡片和下一页不透明 cursor。
message ListRoomsResponse {
  repeated RoomListItem rooms = 1;
  string next_cursor = 2;
}

// GetMyRoomRequest 查询当前用户自己创建的房间。
// 该查询必须由 gateway 写入鉴权后的 owner_user_id，不能从客户端接收用户 ID。
message GetMyRoomRequest {
  RequestMeta meta = 1;
  int64 owner_user_id = 2;
}

// GetMyRoomResponse 返回 Mine 顶部“我的房间”权威卡片。
// 它直接读取 rooms 元数据并用 Room Cell 快照补充实时卡片字段，不依赖发现页投影是否已经写入。
message GetMyRoomResponse {
  bool has_room = 1;
  RoomListItem room = 2;
  int64 server_time_ms = 3;
}

// GetCurrentRoomRequest 查询当前用户是否仍有可恢复的房间 presence。
// 该查询必须由 gateway 写入鉴权后的 user_id，客户端不能提交 user_id。
message GetCurrentRoomRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
}

// GetCurrentRoomResponse 是 App 启动、回前台和网络恢复时的房间恢复探测结果。
// 它只表达是否需要恢复，不会隐式 JoinRoom 或刷新 presence。
message GetCurrentRoomResponse {
  bool has_current_room = 1;
  string room_id = 2;
  int64 room_version = 3;
  string role = 4;
  string mic_session_id = 5;
  string publish_state = 6;
  bool need_join_im_group = 7;
  bool need_rtc_token = 8;
  int64 server_time_ms = 9;
}

// GetRoomSnapshotRequest 主动读取当前房间完整快照。
// 该查询必须由 gateway 写入鉴权后的 viewer_user_id，客户端不能提交 viewer_user_id。
message GetRoomSnapshotRequest {
  RequestMeta meta = 1;
  string room_id = 2;
  int64 viewer_user_id = 3;
}

// GetRoomSnapshotResponse 返回 Room Cell 当前快照；只读，不刷新 presence。
message GetRoomSnapshotResponse {
  RoomSnapshot room = 1;
  int64 server_time_ms = 2;
}

// ListRoomOnlineUsersRequest 分页查询房间在线用户，不让客户端拉完整 RoomSnapshot 做列表分页。
message ListRoomOnlineUsersRequest {
  RequestMeta meta = 1;
  string room_id = 2;
  int64 viewer_user_id = 3;
  int32 page = 4;
  int32 page_size = 5;
}

message ListRoomOnlineUsersResponse {
  repeated RoomUser users = 1;
  int64 total = 2;
  int32 page = 3;
  int32 page_size = 4;
  int64 server_time_ms = 5;
}

// RoomCommandService 承载所有会改变房间状态的命令。
service RoomCommandService {
  rpc CreateRoom(CreateRoomRequest) returns (CreateRoomResponse);
  rpc UpdateRoomProfile(UpdateRoomProfileRequest) returns (UpdateRoomProfileResponse);
  rpc JoinRoom(JoinRoomRequest) returns (JoinRoomResponse);
  rpc RoomHeartbeat(RoomHeartbeatRequest) returns (RoomHeartbeatResponse);
  rpc LeaveRoom(LeaveRoomRequest) returns (LeaveRoomResponse);
  rpc CloseRoom(CloseRoomRequest) returns (CloseRoomResponse);
  rpc MicUp(MicUpRequest) returns (MicUpResponse);
  rpc MicDown(MicDownRequest) returns (MicDownResponse);
  rpc ChangeMicSeat(ChangeMicSeatRequest) returns (ChangeMicSeatResponse);
  rpc ConfirmMicPublishing(ConfirmMicPublishingRequest) returns (ConfirmMicPublishingResponse);
  rpc SetMicMute(SetMicMuteRequest) returns (SetMicMuteResponse);
  rpc ApplyRTCEvent(ApplyRTCEventRequest) returns (ApplyRTCEventResponse);
  rpc SetMicSeatLock(SetMicSeatLockRequest) returns (SetMicSeatLockResponse);
  rpc SetChatEnabled(SetChatEnabledRequest) returns (SetChatEnabledResponse);
  rpc SetRoomAdmin(SetRoomAdminRequest) returns (SetRoomAdminResponse);
  rpc MuteUser(MuteUserRequest) returns (MuteUserResponse);
  rpc KickUser(KickUserRequest) returns (KickUserResponse);
  rpc UnbanUser(UnbanUserRequest) returns (UnbanUserResponse);
  rpc SystemEvictUser(SystemEvictUserRequest) returns (SystemEvictUserResponse);
  rpc SendGift(SendGiftRequest) returns (SendGiftResponse);
}

// RoomGuardService 提供给腾讯云 IM 回调或 gateway 做实时守卫查询。
service RoomGuardService {
  rpc CheckSpeakPermission(CheckSpeakPermissionRequest) returns (CheckSpeakPermissionResponse);
  rpc VerifyRoomPresence(VerifyRoomPresenceRequest) returns (VerifyRoomPresenceResponse);
}

// RoomQueryService 承载不会改变 Room Cell 状态的读模型查询。
service RoomQueryService {
  rpc ListRooms(ListRoomsRequest) returns (ListRoomsResponse);
  rpc ListRoomFeeds(ListRoomFeedsRequest) returns (ListRoomsResponse);
  rpc GetMyRoom(GetMyRoomRequest) returns (GetMyRoomResponse);
  rpc GetCurrentRoom(GetCurrentRoomRequest) returns (GetCurrentRoomResponse);
  rpc GetRoomSnapshot(GetRoomSnapshotRequest) returns (GetRoomSnapshotResponse);
  rpc ListRoomOnlineUsers(ListRoomOnlineUsersRequest) returns (ListRoomOnlineUsersResponse);
}
