syntax = "proto3";
package user;

service UserService {
  rpc GetUser (GetUserRequest) returns (UserResponse);
  // CRM admin: получить поля пользователя для формы редактирования.
  rpc AdminGetUserForUpdate (AdminGetUserForUpdateRequest) returns (AdminUserForUpdateResponse);
  // CRM admin: создать пользователя и вернуть данные для формы.
  rpc AdminCreateUser (AdminCreateUserRequest) returns (AdminUserForUpdateResponse);
  // CRM admin: найти пользователя-реферера по персональному реф. коду.
  rpc AdminFindUserByRefCode (AdminFindUserByRefCodeRequest) returns (AdminReferrerLookupResponse);
  rpc UpdateUser (UpdateUserRequest) returns (UserResponse);
  // CRM admin: обновить редактируемые поля пользователя.
  rpc AdminUpdateUserProfile (AdminUpdateUserProfileRequest) returns (AdminUserForUpdateResponse);
  rpc UpdateUserStatus (UpdateUserStatusRequest) returns (UserResponse);
  rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
  rpc ListUserSources (ListUserSourcesRequest) returns (ListUserSourcesResponse);
  rpc UpdateUserSearchStats (UpdateUserSearchStatsRequest) returns (UpdateUserSearchStatsResponse);
  // Батч-резолв полных имен пользователей по их id (для enrichment в других сервисах).
  rpc GetUserFullNamesByIds (GetUserFullNamesByIdsRequest) returns (GetUserFullNamesByIdsResponse);
  // Ровно одно из полей: user_id, email, phone, telegram_username → id получателя для внутреннего перевода.
  rpc ResolveUserForTransfer (ResolveUserForTransferRequest) returns (ResolveUserForTransferResponse);
  // CRM Arbi Exchange: логин сервисным аккаунтом (env), OTP из TOTP-секрета; session_id для X-Session-ID.
  rpc GetCrmSession (GetCrmSessionRequest) returns (GetCrmSessionResponse);
  // HTTP к CRM Exchange: сессия и X-Session-ID только внутри user-service.
  rpc CrmExchangeHttp (CrmExchangeHttpRequest) returns (CrmExchangeHttpResponse);
  // Создание/получение CRM-клиента по wallet_user_id и создание заявки на обмен (два шага к CRM внутри сервиса).
  rpc SubmitWalletCrmExchangeOrder (SubmitWalletCrmExchangeOrderRequest) returns (SubmitWalletCrmExchangeOrderResponse);
  // Уникальные валютные пары из последних заявок на обмен (по БД user-service), без дубликатов пар.
  rpc ListWalletExchangeRecentPairs (ListWalletExchangeRecentPairsRequest) returns (ListWalletExchangeRecentPairsResponse);
  // Сообщение менеджера клиенту: публикация в Rabbit app.notification → gateway → Socket.IO `notification`.
  rpc ForwardManagerChatMessage (ForwardManagerChatMessageRequest) returns (ForwardManagerChatMessageResponse);
  // Удаление сообщения из Monica CRM: soft-delete по message_id + Socket.IO `notification` с is_deleted.
  rpc DeleteManagerChatMessage (DeleteManagerChatMessageRequest) returns (DeleteManagerChatMessageResponse);
  // Чат AlexAI (chat-analytics): POST /api/v1/arbi-wallet/messages; CRM session только на user-service.
  rpc PostArbiWalletChatMessage (PostArbiWalletChatMessageRequest) returns (PostArbiWalletChatMessageResponse);
  // Чат AlexAI (chat-analytics): GET /api/v1/arbi-wallet/messages?wallet_client_id=… — список сообщений пользователя.
  rpc ListArbiWalletChatMessages (ListArbiWalletChatMessagesRequest) returns (ListArbiWalletChatMessagesResponse);
  // Прокси медиа по HTTPS URL (allowlist): CRM session + X-Session-ID только в user-service; тело для отдачи на gateway → FE.
  rpc ProxyArbiWalletChatMedia (ProxyArbiWalletChatMediaRequest) returns (ProxyArbiWalletChatMediaResponse);
  // Локально в user-service: последний прочитанный message_id из chat-analytics (read receipt).
  rpc GetSupportChatLastRead (GetSupportChatLastReadRequest) returns (GetSupportChatLastReadResponse);
  rpc SetSupportChatLastRead (SetSupportChatLastReadRequest) returns (SetSupportChatLastReadResponse);
  // Telegram bot microservice: upsert user by telegram_id (registration TELEGRAM).
  rpc EnsureTelegramUser (EnsureTelegramUserRequest) returns (TelegramUserLanguageResponse);
  // Monika-compatible customer create endpoint payload (local Wallet source of truth).
  rpc CreateMonikaCustomer (CreateMonikaCustomerRequest) returns (CreateMonikaCustomerResponse);
  // Monika-compatible customer lookup by filter_by or customer id.
  rpc FindMonikaCustomer (FindMonikaCustomerRequest) returns (FindMonikaCustomerResponse);
  // Monika-compatible UTM session create/get.
  rpc CreateMonikaSessionUtm (CreateMonikaSessionUtmRequest) returns (CreateMonikaSessionUtmResponse);
  rpc GetMonikaSessionUtmById (GetMonikaSessionUtmByIdRequest) returns (GetMonikaSessionUtmByIdResponse);
  rpc ListMonikaSessionUtms (ListMonikaSessionUtmsRequest) returns (ListMonikaSessionUtmsResponse);
  rpc PatchMonikaSessionUtm (PatchMonikaSessionUtmRequest) returns (PatchMonikaSessionUtmResponse);
  // Monika-compatible adv channel groups CRUD.
  rpc ListMonikaAdvChannelGroups (ListMonikaAdvChannelGroupsRequest) returns (ListMonikaAdvChannelGroupsResponse);
  rpc CreateMonikaAdvChannelGroup (CreateMonikaAdvChannelGroupRequest) returns (CreateMonikaAdvChannelGroupResponse);
  rpc PatchMonikaAdvChannelGroup (PatchMonikaAdvChannelGroupRequest) returns (PatchMonikaAdvChannelGroupResponse);
  rpc DeleteMonikaAdvChannelGroup (DeleteMonikaAdvChannelGroupRequest) returns (DeleteMonikaAdvChannelGroupResponse);
  // Monika-compatible adv channels CRUD.
  rpc ListMonikaAdvChannels (ListMonikaAdvChannelsRequest) returns (ListMonikaAdvChannelsResponse);
  rpc GetMonikaAdvChannelById (GetMonikaAdvChannelByIdRequest) returns (GetMonikaAdvChannelByIdResponse);
  rpc CreateMonikaAdvChannel (CreateMonikaAdvChannelRequest) returns (CreateMonikaAdvChannelResponse);
  rpc PatchMonikaAdvChannel (PatchMonikaAdvChannelRequest) returns (PatchMonikaAdvChannelResponse);
  rpc DeleteMonikaAdvChannel (DeleteMonikaAdvChannelRequest) returns (DeleteMonikaAdvChannelResponse);
  // Monika-compatible links for TG bot with UTM.
  rpc ListMonikaTgLinksWithUtm (ListMonikaTgLinksWithUtmRequest) returns (ListMonikaTgLinksWithUtmResponse);
  rpc CreateMonikaTgLinkWithUtm (CreateMonikaTgLinkWithUtmRequest) returns (CreateMonikaTgLinkWithUtmResponse);
  rpc UpdateLanguageByTelegramId (UpdateLanguageByTelegramIdRequest) returns (TelegramUserLanguageResponse);
  // Реферальная ссылка MONETA по telegram_id → ref_code → https://arbi.exchange/contacts/qr?code=…
  rpc GetReferralLinkByTelegramId (GetReferralLinkByTelegramIdRequest) returns (GetReferralLinkByTelegramIdResponse);
  // Реферальная ссылка: TELEGRAM users → CRM by telegram_id; остальные → текущий CRM customer id.
  rpc GetReferralLinkByUserId (GetReferralLinkByUserIdRequest) returns (GetReferralLinkByTelegramIdResponse);
  // Реферальный баланс: TELEGRAM users → CRM by telegram_id; остальные → текущий CRM customer id.
  rpc GetReferralBalanceByUserId (GetReferralBalanceByUserIdRequest) returns (GetReferralBalanceByUserIdResponse);
  // Смена email: отправка кода на новый адрес (RabbitMQ → notification).
  rpc RequestChangeEmailCode (RequestChangeEmailCodeRequest) returns (ContactChangeMessageResponse);
  rpc VerifyChangeEmailCode (VerifyChangeEmailCodeRequest) returns (UserResponse);
  // Смена phone: отправка SMS-кода через Twilio.
  rpc RequestChangePhoneCode (RequestChangePhoneCodeRequest) returns (ContactChangeMessageResponse);
  rpc VerifyChangePhoneCode (VerifyChangePhoneCodeRequest) returns (UserResponse);
  // Батч-поиск пользователей по строке (telegram_username, email, phone, whatsapp): возвращает только id.
  rpc BatchGetUserIdsBySearch (BatchGetUserIdsBySearchRequest) returns (BatchGetUserIdsBySearchResponse);
  // Батч-поиск пользователей по adv channel (name/code/legacy string): возвращает только id.
  rpc BatchGetUserIdsByAdvChannel (BatchGetUserIdsByAdvChannelRequest) returns (BatchGetUserIdsByAdvChannelResponse);
  // Поиск пользователей по строке (telegram_username, email, phone, whatsapp): возвращает до 50 совпадений.
  rpc SearchUser (SearchUserRequest) returns (SearchUserResponse);
  // Прелоад выбранного пользователя по id для поиска пользователей: возвращает один элемент.
  rpc PrealoadUserForSearch (PrealoadUserForSearchRequest) returns (PrealoadUserForSearchResponse);
  // Батч-получение публичных данных пользователей по списку id (для enrichment в других сервисах).
  rpc BatchGetUsersByIds (BatchGetUsersByIdsRequest) returns (BatchGetUsersByIdsResponse);
  rpc GetFinModelRegistrationStats (GetFinModelRegistrationStatsRequest) returns (GetFinModelRegistrationStatsResponse);
}

enum UserStatus {
  ACTIVE = 0;
  BANNED = 1;
}

enum UserKycStatus {
  UNVERIFIED = 0;
  NOT_STARTED = 1;
  IN_PROGRESS = 2;
  PENDING_REVIEW = 3;
  APPROVED = 4;
  RETRY_REQUIRED = 5;
  REJECTED = 6;
}

enum UserSearchBonusType {
  PROFIT = 0;
  VOLUME = 1;
}

message GetUserRequest {
  int32 id = 1;
}

message UserResponse {
  int32 id = 1;
  int32 uid = 13;
  optional string email = 2;
  bool email_verified = 18;
  optional string phone = 3;
  bool phone_verified = 19;
  optional string telegram_id = 4;
  optional string telegram_username = 5;
  optional string apple_sub = 6;
  string registration_type = 7;
  UserStatus status = 14;
  optional string first_name = 8;
  optional string last_name = 9;
  optional string avatar = 10;
  string created_at = 11;
  string updated_at = 12;
  repeated UserBalance balances = 15;
  UserKycStatus kyc_status = 16;
  UserKycInfo kyc = 17;
  optional string card_first_name = 20;
  optional string card_last_name = 21;
  optional int32 crm_customer_id = 22;
  optional int32 referrer_user_id = 23;
  optional double ref_percent = 24;
  optional double referral_volume = 25;
  optional int32 exchanges_count = 26;
  optional int32 exchange_count_last_year = 27;
  optional double total_amount = 28;
  optional double avg_check = 29;
  optional string first_exchange_at = 30;
  optional string last_exchange_at = 31;
  optional double life_period = 32;
  optional double life_period_year = 33;
  optional double frequency = 34;
  optional double ltv = 35;
  optional string user_search_updated_at = 36;
  optional UserSearchBonusType bonus_type = 37;
  optional string referrer_name = 38;
  optional int32 exchange_count_last_month = 39;
  optional double total_profit = 40;
  optional string ref_code = 41;
  optional string adv_channel_name = 42;
  optional bool is_partner = 43;
  optional string attention = 44;
  optional bool ban = 45;
  optional string comment = 46;
  optional string instagram = 47;
  optional string facebook = 48;
  optional string facebook_url = 49;
  optional string utm_data_json = 50;
  optional bool block_in_tg = 51;
}

message AdminGetUserForUpdateRequest {
  int32 user_id = 1;
}

message AdminUserForUpdateResponse {
  int32 id = 1;
  int32 uid = 2;
  optional string first_name = 3;
  optional string last_name = 4;
  optional string email = 5;
  optional string phone = 6;
  optional string telegram_username = 7;
  optional string telegram_id = 8;
  optional string whatsapp = 9;
  optional string adv_channel = 10;
  optional string ref_code = 11;
  optional int32 referrer_user_id = 12;
  // Строковый тип для admin-flow, чтобы избежать тихого enum-default при межсервисном проксировании.
  // Допустимые значения: PROFIT | VOLUME | REFERRAL_BONUS_TYPE_PROFIT | REFERRAL_BONUS_TYPE_VOLUME.
  optional string bonus_type = 13;
  optional double ref_percent = 14;
  // true, если в рамках AdminUpdateUserProfile был установлен referrer_user_id.
  bool referrer_changed = 15;
  optional bool is_partner = 16;
  optional string attention = 17;
  optional bool ban = 18;
  optional string comment = 19;
  optional string instagram = 20;
  optional string facebook = 21;
  optional string facebook_url = 22;
  optional int32 adv_channel_id = 23;
}

message AdminUpdateUserProfileRequest {
  int32 user_id = 1;
  optional string first_name = 2;
  optional string last_name = 3;
  optional string email = 4;
  optional string phone = 5;
  optional string telegram_username = 6;
  optional string telegram_id = 7;
  optional string whatsapp = 8;
  optional string adv_channel = 9;
  optional string ref_code = 10;
  // Разрешено только если у пользователя еще нет реферера.
  // Если текущий реферер уже задан, сервис возвращает ошибку REFERRER_ALREADY_SET.
  optional int32 referrer_user_id = 11;
  // Строковый тип для admin-flow, чтобы избежать тихого enum-default при межсервисном проксировании.
  // Допустимые значения: PROFIT | VOLUME | REFERRAL_BONUS_TYPE_PROFIT | REFERRAL_BONUS_TYPE_VOLUME.
  optional string bonus_type = 12;
  optional double ref_percent = 13;
  optional bool is_partner = 14;
  optional string attention = 15;
  optional bool ban = 16;
  optional string comment = 17;
  optional string instagram = 18;
  optional string facebook = 19;
  optional string facebook_url = 20;
  // true — в этом запросе нужно выполнить изменение referrer_user_id;
  // false/отсутствует — referrer_user_id игнорируется и не должен вызывать REFERRER_ALREADY_SET.
  optional bool referrer_changed = 21;
  optional int32 adv_channel_id = 22;
}

message AdminCreateUserRequest {
  optional string first_name = 1;
  optional string last_name = 2;
  optional string email = 3;
  optional string phone = 4;
  optional string telegram_username = 5;
  optional string telegram_id = 6;
  optional string whatsapp = 7;
  optional string adv_channel = 8;
  optional string ref_code = 9;
  optional int32 referrer_user_id = 10;
  // Строковый тип для admin-flow, чтобы избежать тихого enum-default при межсервисном проксировании.
  // Допустимые значения: PROFIT | VOLUME | REFERRAL_BONUS_TYPE_PROFIT | REFERRAL_BONUS_TYPE_VOLUME.
  optional string bonus_type = 11;
  optional double ref_percent = 12;
  optional bool is_partner = 13;
  optional string attention = 14;
  optional bool ban = 15;
  optional string comment = 16;
  optional string instagram = 17;
  optional string facebook = 18;
  optional string facebook_url = 19;
  optional int32 adv_channel_id = 20;
}

message AdminFindUserByRefCodeRequest {
  string ref_code = 1;
}

message AdminReferrerLookupResponse {
  int32 user_id = 1;
  int32 uid = 2;
  optional string first_name = 3;
  optional string last_name = 4;
  optional string full_name = 5;
  optional string telegram_username = 6;
  optional string ref_code = 7;
}

message UpdateUserRequest {
  int32 user_id = 1;
  optional string first_name = 2;
  optional string last_name = 3;
  optional string avatar = 4;
  optional string card_first_name = 5;
  optional string card_last_name = 6;
}

message UpdateUserStatusRequest {
  int32 user_id = 1;
  UserStatus status = 2;
}

enum UserSortBy {
  CREATED_AT = 0;
  STATUS = 1;
  REGISTRATION_TYPE = 2;
}

enum SortOrder {
  DESC = 0;
  ASC = 1;
}

message ListUsersRequest {
  optional int32 page = 1;
  optional int32 limit = 2;
  optional UserSortBy sort_by = 3;
  optional SortOrder order = 4;
  optional string search = 5;
  optional UserStatus status_value = 6;
  optional string registration_type_value = 7;
  repeated string sources = 8;
  repeated string tags = 9;
  optional int32 referrer_user_id = 10;
  optional string registration_from = 11;
  optional string registration_to = 12;
  optional string deal_from = 13;
  optional string deal_to = 14;
  optional string last_deal_from = 15;
  optional string last_deal_to = 16;
  optional string sort = 17;
}

message ListUsersResponse {
  repeated UserResponse items = 1;
  int32 all_count = 2;
  int32 page = 3;
  int32 limit = 4;
}

message ListUserSourcesRequest {}

message UserSourceItem {
  int32 id = 1;
  string name = 2;
  optional string code = 3;
}

message ListUserSourcesResponse {
  repeated UserSourceItem sources = 1;
}

message UserSearchExchangeStats {
  int32 exchanges_count = 1;
  int32 exchange_count_last_year = 2;
  double total_amount = 3;
  double avg_check = 4;
  optional string first_exchange_at = 5;
  optional string last_exchange_at = 6;
  optional double life_period = 7;
  optional double life_period_year = 8;
  optional double frequency = 9;
  optional double ltv = 10;
}

message UserSearchReferralStats {
  optional int32 referrer_user_id = 1;
  optional double ref_percent = 2;
  double referral_volume = 3;
  optional UserSearchBonusType bonus_type = 4;
}

message UserSearchStatsUpdate {
  int32 user_id = 1;
  UserSearchExchangeStats exchange_stats = 2;
  UserSearchReferralStats referral_stats = 3;
}

message UpdateUserSearchStatsRequest {
  repeated UserSearchStatsUpdate items = 1;
}

message UpdateUserSearchStatsResponse {
  int32 updated_count = 1;
}

message GetUserFullNamesByIdsRequest {
  repeated int32 user_ids = 1;
}

message UserFullNameById {
  int32 user_id = 1;
  optional string full_name = 2;
}

message GetUserFullNamesByIdsResponse {
  repeated UserFullNameById items = 1;
  repeated int32 not_found_user_ids = 2;
}

message ResolveUserForTransferRequest {
  optional int32 user_id = 1;
  optional string email = 2;
  optional string phone = 3;
  optional string telegram_username = 4;
  optional string uid = 5;
}

message ResolveUserForTransferResponse {
  int32 id = 1;
}

message GetCrmSessionRequest {
  bool force_refresh = 1;
}

message GetCrmSessionResponse {
  string session_id = 1;
  int64 expires_at_unix = 2;
}

message CrmExchangeHttpRequest {
  string method = 1;
  string path = 2;
  optional string query_string = 3;
  optional string json_body = 4;
}

message CrmExchangeHttpResponse {
  int32 http_status = 1;
  string body_json = 2;
}

message SubmitWalletCrmExchangeOrderRequest {
  int32 wallet_user_id = 1;
  double input_amount = 2;
  double output_amount = 3;
  int32 input_currency = 4;
  int32 output_currency = 5;
  int32 withdraw_type = 6;
  optional int32 receive_bank = 7;
  optional double exchange_rate_display = 8;
  optional string send_currency_short = 9;
  optional string receive_currency_short = 10;
}

message SubmitWalletCrmExchangeOrderResponse {
  bool success = 1;
  optional int32 order_id = 2;
  optional string message = 3;
}

message ListWalletExchangeRecentPairsRequest {
  int32 wallet_user_id = 1;
  // По умолчанию 20, максимум обрезается на сервисе.
  int32 limit = 2;
}

message WalletExchangeRecentPairItem {
  string send_currency_short = 1;
  string receive_currency_short = 2;
  double last_input_amount = 3;
  int64 last_submitted_at_unix_ms = 4;
}

message ListWalletExchangeRecentPairsResponse {
  repeated WalletExchangeRecentPairItem pairs = 1;
}

message ForwardManagerChatMessageRequest {
  string forward_secret = 1;
  string text = 2;
  bool is_echo = 3;
  string wallet_client_id = 4;
  optional string wallet_manager_id = 5;
  optional string datetime = 6;
  optional string content = 7;
  optional string message_id = 8;
  // JSON для reply_to: отсутствие поля — нет ответа; строка "null" — null; иначе JSON-значение.
  optional string reply_to_json = 9;
}

message ForwardManagerChatMessageResponse {
  bool ok = 1;
}

message DeleteManagerChatMessageRequest {
  string message_id = 1;
}

message DeleteManagerChatMessageResponse {
  bool ok = 1;
  optional string message = 2;
}

message PostArbiWalletChatMessageRequest {
  int32 wallet_user_id = 1;
  // Частичное тело от клиента: text, content, reply_to, datetime (ISO UTC).
  string json_body = 2;
  optional bytes media_file = 3;
  optional string media_filename = 4;
  optional string media_content_type = 5;
}

message PostArbiWalletChatMessageResponse {
  int32 http_status = 1;
  string body_json = 2;
}

message ListArbiWalletChatMessagesRequest {
  int32 wallet_user_id = 1;
  optional int32 limit = 2;
  optional int32 offset = 3;
}

message ListArbiWalletChatMessagesResponse {
  int32 http_status = 1;
  string body_json = 2;
}

message ProxyArbiWalletChatMediaRequest {
  int32 wallet_user_id = 1;
  string url = 2;
}

message ProxyArbiWalletChatMediaResponse {
  int32 http_status = 1;
  bytes body = 2;
  string content_type = 3;
}

message GetSupportChatLastReadRequest {
  int32 wallet_user_id = 1;
}

message GetSupportChatLastReadResponse {
  int32 last_read_message_id = 1;
}

message SetSupportChatLastReadRequest {
  int32 wallet_user_id = 1;
  int32 last_read_message_id = 2;
}

message SetSupportChatLastReadResponse {
  bool ok = 1;
}

message EnsureTelegramUserRequest {
  string telegram_id = 1;
  optional string telegram_username = 2;
  optional string first_name = 3;
  optional string last_name = 4;
}

message CreateMonikaCustomerRequest {
  optional string telegram_id = 1;
  optional string whatsapp = 2;
  optional string telegram_username = 3;
  optional string username = 4;
  optional string first_name = 5;
  optional string last_name = 6;
  optional string ref_code = 7;
  optional string adv_channel = 8;
  optional string utm_id = 9;
  optional string code = 10;
  optional string utm_data_json = 11;
}

message CreateMonikaCustomerResponse {
  bool success = 1;
  string jwt = 2;
}

message FindMonikaCustomerRequest {
  string lookup_value = 1;
  optional string filter_by = 2;
}

message FindMonikaCustomerResponse {
  bool success = 1;
  optional string jwt = 2;
}

message CreateMonikaSessionUtmRequest {
  optional string utm_data_json = 1;
  optional string client_id = 2;
  optional string g_client_id = 3;
  optional string gclid = 4;
}

message CreateMonikaSessionUtmResponse {
  bool success = 1;
  string jwt = 2;
}

message GetMonikaSessionUtmByIdRequest {
  int32 id = 1;
}

message GetMonikaSessionUtmByIdResponse {
  bool success = 1;
  string jwt = 2;
}

message ListMonikaSessionUtmsRequest {}

message ListMonikaSessionUtmsResponse {
  bool success = 1;
  string jwt = 2;
}

message PatchMonikaSessionUtmRequest {
  int32 id = 1;
  optional string utm_data_json = 2;
  optional string client_id = 3;
  optional string g_client_id = 4;
  optional string gclid = 5;
}

message PatchMonikaSessionUtmResponse {
  bool success = 1;
  string jwt = 2;
}

message ListMonikaAdvChannelGroupsRequest {}

message CreateMonikaAdvChannelGroupRequest {
  string name = 1;
}

message PatchMonikaAdvChannelGroupRequest {
  int32 id = 1;
  optional string name = 2;
}

message DeleteMonikaAdvChannelGroupRequest {
  int32 id = 1;
}

message ListMonikaAdvChannelGroupsResponse {
  bool success = 1;
  string jwt = 2;
}

message CreateMonikaAdvChannelGroupResponse {
  bool success = 1;
  string jwt = 2;
}

message PatchMonikaAdvChannelGroupResponse {
  bool success = 1;
  string jwt = 2;
}

message DeleteMonikaAdvChannelGroupResponse {
  bool success = 1;
  string jwt = 2;
}

message ListMonikaAdvChannelsRequest {
  optional string search = 1;
  optional int32 group_id = 2;
  optional int32 partner_id = 3;
}

message GetMonikaAdvChannelByIdRequest {
  int32 id = 1;
}

message CreateMonikaAdvChannelRequest {
  string name = 1;
  string code = 2;
  optional int32 group_id = 3;
  optional string url_list = 4;
  optional bool is_website = 5;
  optional bool marketing_report = 6;
  optional int32 partner_id = 7;
}

message PatchMonikaAdvChannelRequest {
  int32 id = 1;
  optional string name = 2;
  optional string code = 3;
  optional int32 group_id = 4;
  optional string url_list = 5;
  optional bool is_website = 6;
  optional bool marketing_report = 7;
  optional int32 partner_id = 8;
}

message DeleteMonikaAdvChannelRequest {
  int32 id = 1;
}

message ListMonikaAdvChannelsResponse {
  bool success = 1;
  string jwt = 2;
}

message GetMonikaAdvChannelByIdResponse {
  bool success = 1;
  string jwt = 2;
}

message CreateMonikaAdvChannelResponse {
  bool success = 1;
  string jwt = 2;
}

message PatchMonikaAdvChannelResponse {
  bool success = 1;
  string jwt = 2;
}

message DeleteMonikaAdvChannelResponse {
  bool success = 1;
  string jwt = 2;
}

message ListMonikaTgLinksWithUtmRequest {
  optional int32 page = 1;
  optional int32 limit = 2;
}

message CreateMonikaTgLinkWithUtmRequest {
  string utm_queries = 1;
}

message ListMonikaTgLinksWithUtmResponse {
  bool success = 1;
  string jwt = 2;
}

message CreateMonikaTgLinkWithUtmResponse {
  bool success = 1;
  string jwt = 2;
}

message UpdateLanguageByTelegramIdRequest {
  string telegram_id = 1;
  string language = 2;
}

message TelegramUserLanguageResponse {
  int32 id = 1;
  optional string language = 2;
}

message GetReferralLinkByTelegramIdRequest {
  string telegram_id = 1;
}

message GetReferralLinkByUserIdRequest {
  int32 user_id = 1;
}

message GetReferralLinkByTelegramIdResponse {
  string referral_link = 1;
  string ref_code = 2;
}

message GetReferralBalanceByUserIdRequest {
  int32 user_id = 1;
}

message ReferralLevels {
  double level_1 = 1;
  double level_2 = 2;
  double level_3 = 3;
  double level_4 = 4;
  double level_5 = 5;
}

message ReferralPartner {
  int32 level = 1;
  int32 id = 2;
  optional string telegram_id = 3;
  optional string email = 4;
  optional int32 wallet_id = 5;
  optional string username = 6;
  optional string first_name = 7;
  optional string last_name = 8;
  optional string full_name = 9;
  optional string created_at = 10;
}

message GetReferralBalanceByUserIdResponse {
  string total_usdt_balance = 1;
  string available_usdt_balance = 2;
  int32 referrals_count = 3;
  double ref_percent = 4;
  bool is_multilevel = 5;
  ReferralLevels levels = 6;
  repeated ReferralPartner referrals = 7;
}

message RequestChangeEmailCodeRequest {
  int32 user_id = 1;
  string email = 2;
}

message VerifyChangeEmailCodeRequest {
  int32 user_id = 1;
  string email = 2;
  string code = 3;
}

message RequestChangePhoneCodeRequest {
  int32 user_id = 1;
  string phone = 2;
}

message VerifyChangePhoneCodeRequest {
  int32 user_id = 1;
  string phone = 2;
  string code = 3;
}

message ContactChangeMessageResponse {
  string message = 1;
}

message BatchGetUserIdsBySearchRequest {
  string search = 1;
  // Максимум возвращаемых id; сервис может дополнительно ограничить.
  int32 limit = 2;
}

message BatchGetUserIdsBySearchResponse {
  repeated int32 user_ids = 1;
  // true, если совпадений больше, чем limit (результат был обрезан).
  bool too_many_matches = 2;
  int32 total_matches = 3;
}

message BatchGetUserIdsByAdvChannelRequest {
  string adv_channel = 1;
  // Максимум возвращаемых id; сервис может дополнительно ограничить.
  int32 limit = 2;
}

message BatchGetUserIdsByAdvChannelResponse {
  repeated int32 user_ids = 1;
  // true, если совпадений больше, чем limit (результат был обрезан).
  bool too_many_matches = 2;
  int32 total_matches = 3;
}

message SearchUserRequest {
  // Строка поиска по полям: telegram_username, email, phone, whatsapp.
  string search = 1;
  // По умолчанию 50, максимум 50.
  int32 limit = 2;
}

message SearchUserItem {
  int32 id = 1;
  int32 uid = 2;
  optional string first_name = 3;
  optional string last_name = 4;
  optional string email = 5;
  optional string phone = 6;
  optional string telegram_username = 7;
  optional string whatsapp = 8;
}

message SearchUserResponse {
  repeated SearchUserItem items = 1;
}

message PrealoadUserForSearchRequest {
  int32 user_id = 1;
}

message PrealoadUserForSearchResponse {
  optional SearchUserItem item = 1;
}

message BatchGetUsersByIdsRequest {
  repeated int32 user_ids = 1;
}

message BatchUserPublicItem {
  int32 id = 1;
  int32 uid = 2;
  optional string first_name = 3;
  optional string last_name = 4;
  optional string avatar = 5;
  optional string telegram_username = 6;
  UserStatus status = 7;
  optional int32 crm_customer_id = 8;
  optional bool is_partner = 9;
}

message BatchGetUsersByIdsResponse {
  repeated BatchUserPublicItem items = 1;
}

message GetFinModelRegistrationStatsRequest {
  string date_from = 1;
  string date_to = 2;
  optional int32 partner_id = 3;
}

message GetFinModelRegistrationStatsResponse {
  int32 total = 1;
  int32 has_deal = 2;
}

message UserBalance {
  string asset = 1;
  string network = 2;
  string available = 3;
  string reserved = 4;
  string total = 5;
}

message UserKycInfo {
  string user_id = 1;
  string applicant_id = 2;
  UserKycStatus kyc_status = 3;
  string review_answer = 4;
  repeated string rejection_labels = 5;
  string moderation_comment = 6;
  int64 created_at_ms = 7;
  int64 updated_at_ms = 8;
}
