// Protocol Buffer Definition: FreeExile 6-Player Party System & Scaling Engine
// Schema-First Specification for Party Lifecycle, PoE2 Difficulty/Loot Scaling,
// and Loot Allocation Modes. Adheres strictly to Zero-Trust Server Authority.

syntax = "proto3";

package freeexile.party;

option go_package = "freeexile/party";
option java_package = "com.freeexile.proto.party";
option csharp_namespace = "FreeExile.Proto.Party";

// Loot Allocation Modes (3 Canonical PoE2 Modes)
enum LootAllocationMode {
  ALLOCATION_UNSPECIFIED = 0;
  PERMANENT = 1;     // Khóa vật phẩm vĩnh viễn cho người chơi được gán
  SHORT = 2;         // Khóa 5.0s cho người được gán, sau đó chuyển sang tự do nhặt
  FREE_FOR_ALL = 3;  // Toàn bộ thành viên nhặt tự do ngay khi rơi
}

// Party Member Roles
enum PartyRole {
  ROLE_UNSPECIFIED = 0;
  LEADER = 1;
  MEMBER = 2;
}

// Error Codes for Party Operations
enum PartyErrorCode {
  PARTY_OK = 0;
  PARTY_ERR_NOT_IN_PARTY = 1;
  PARTY_ERR_ALREADY_IN_PARTY = 2;
  PARTY_ERR_PARTY_FULL = 3;
  PARTY_ERR_NOT_LEADER = 4;
  PARTY_ERR_TARGET_OFFLINE = 5;
  PARTY_ERR_TARGET_BLOCKED = 6;
  PARTY_ERR_REALM_MISMATCH = 7;
  PARTY_ERR_INVITE_PENDING = 8;
  PARTY_ERR_INVITE_NOT_FOUND = 9;
  PARTY_ERR_TARGET_ALREADY_IN_PARTY = 10;
}

// State Snapshot of an Individual Party Member
message PartyMemberState {
  string player_id = 1;
  string player_name = 2;
  string character_class = 3;
  int32 level = 4;
  PartyRole role = 5;
  string current_zone_id = 6;
  string current_instance_id = 7;
  float pos_x = 8;
  float pos_y = 9;
  float current_hp = 10;
  float max_hp = 11;
  float current_mana = 12;
  float max_mana = 13;
  bool is_nearby = 14;      // true if within nearby_radius (15.0m) and in same zone/instance
  bool is_online = 15;
}

// PoE2 Scaled Difficulty & Reward Multipliers
message PartyScalingStats {
  int32 party_size = 1;                    // Total party members (1..6)
  int32 nearby_members_count = 2;          // Members within 15.0m
  float monster_hp_multiplier = 3;         // 1.0x to 3.5x (+50% per extra member)
  float exp_bonus_pct = 4;                 // +30% per extra nearby member
  float item_quantity_bonus_pct = 5;       // +50% per extra nearby member
  float currency_quantity_bonus_pct = 6;   // +50% per extra nearby member
  float rarity_bonus_pct = 7;              // +30% per extra nearby member
  float nearby_radius = 8;                 // 15.0 world units
}

// Complete Party State
message PartyInfo {
  string party_id = 1;
  string leader_id = 2;
  LootAllocationMode allocation_mode = 3;
  float short_allocation_timeout_sec = 4;  // Default 5.0s for SHORT mode
  repeated PartyMemberState members = 5;   // Up to 6 members
  PartyScalingStats scaling_stats = 6;
  int64 created_at_ms = 7;
}

// Request / Response Messages

message CreatePartyRequest {
  string player_id = 1;
  LootAllocationMode allocation_mode = 2;
}

message CreatePartyResponse {
  bool success = 1;
  PartyInfo party = 2;
  PartyErrorCode error_code = 3;
  string error_message = 4;
}

message InvitePartyRequest {
  string party_id = 1;
  string inviter_id = 2;
  string target_player_id = 3;
}

message InvitePartyResponse {
  bool success = 1;
  string invite_id = 2;
  PartyErrorCode error_code = 3;
  string error_message = 4;
}

message PartyInviteNotice {
  string invite_id = 1;
  string party_id = 2;
  string inviter_id = 3;
  string inviter_name = 4;
  int64 expires_at_ms = 5;
}

message RespondPartyInviteRequest {
  string invite_id = 1;
  string player_id = 2;
  bool accept = 3;
}

message RespondPartyInviteResponse {
  bool success = 1;
  PartyInfo party = 2;
  PartyErrorCode error_code = 3;
  string error_message = 4;
}

message LeavePartyRequest {
  string party_id = 1;
  string player_id = 2;
}

message LeavePartyResponse {
  bool success = 1;
  PartyErrorCode error_code = 2;
  string error_message = 3;
}

message KickPartyMemberRequest {
  string party_id = 1;
  string leader_id = 2;
  string target_player_id = 3;
}

message KickPartyMemberResponse {
  bool success = 1;
  PartyErrorCode error_code = 2;
  string error_message = 3;
}

message PromotePartyLeaderRequest {
  string party_id = 1;
  string current_leader_id = 2;
  string new_leader_id = 3;
}

message PromotePartyLeaderResponse {
  bool success = 1;
  PartyErrorCode error_code = 2;
  string error_message = 3;
}

message DisbandPartyRequest {
  string party_id = 1;
  string leader_id = 2;
}

message DisbandPartyResponse {
  bool success = 1;
  PartyErrorCode error_code = 2;
  string error_message = 3;
}

message SetLootAllocationRequest {
  string party_id = 1;
  string leader_id = 2;
  LootAllocationMode allocation_mode = 3;
}

message SetLootAllocationResponse {
  bool success = 1;
  LootAllocationMode active_mode = 2;
  PartyErrorCode error_code = 3;
  string error_message = 4;
}

// Real-Time Notification: Full Party Synchronization Broadcast
message PartySyncNotice {
  PartyInfo party = 1;
  string event_type = 2;  // "MEMBER_JOINED", "MEMBER_LEFT", "LEADER_CHANGED", "SCALING_UPDATED"
  string subject_player_id = 3;
  int64 timestamp_ms = 4;
}

// Lightweight 30Hz Location & Vitals Sync within Party
message PartyMemberLocationNotice {
  string party_id = 1;
  string player_id = 2;
  string zone_id = 3;
  string instance_id = 4;
  float pos_x = 5;
  float pos_y = 6;
  float current_hp = 7;
  float max_hp = 8;
  bool is_nearby = 9;
}
