using MessagePack; using SVSim.Database.Enums; using System.Text.Json; using System.Text.Json.Serialization; namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses; [MessagePackObject] public class IndexResponse { #region Primitive Returns [JsonPropertyName("spot_point")] [Key("spot_point")] public int SpotPoint { get; set; } [JsonPropertyName("is_available_colosseum_free_entry")] [Key("is_available_colosseum_free_entry")] public bool IsAvailableColosseumFreeEntry { get; set; } [JsonPropertyName("friend_battle_invite_count")] [Key("friend_battle_invite_count")] public int FriendBattleInviteCount { get; set; } [JsonPropertyName("battle_recovery_status")] [Key("battle_recovery_status")] public int BattleRecoveryStatus { get; set; } [JsonPropertyName("room_recovery_status")] [Key("room_recovery_status")] public int RoomRecoveryStatus { get; set; } /// /// Prod emits this as bool (per the 2026-05-23 capture); the spec leaves it as a TODO /// (load-index.md line 296-297). We send bool to match prod; client's `.ToBoolean()` /// path handles either shape, but matching prod avoids the int-vs-bool drift noted in /// the seed-data-strategy crash audit. /// [JsonPropertyName("is_battle_pass_period")] [Key("is_battle_pass_period")] public bool IsBattlePassPeriod { get; set; } [JsonPropertyName("card_set_id_for_resource_dl_view")] [Key("card_set_id_for_resource_dl_view")] public int CardSetIdForResourceDlView { get; set; } // Serialized as wire deck_format via FormatJsonConverter (registered in Program.cs). [JsonPropertyName("deck_format")] [Key("deck_format")] public Format DeckFormat { get; set; } = Format.Rotation; #endregion #region Basic User Data [JsonPropertyName("user_tutorial")] [Key("user_tutorial")] public UserTutorial UserTutorial { get; set; } = new UserTutorial(); [JsonPropertyName("user_info")] [Key("user_info")] public UserInfo UserInfo { get; set; } = new UserInfo(); [JsonPropertyName("user_crystal_count")] [Key("user_crystal_count")] public UserCurrency UserCurrency { get; set; } = new UserCurrency(); #endregion #region Inventory Data [JsonPropertyName("user_item_list")] [Key("user_item_list")] public List UserItems { get; set; } = new(); [JsonPropertyName("user_deck_rotation")] [Key("user_deck_rotation")] public UserFormatDeckInfo UserRotationDecks { get; set; } = new(); [JsonPropertyName("user_deck_unlimited")] [Key("user_deck_unlimited")] public UserFormatDeckInfo UserUnlimitedDecks { get; set; } = new(); [JsonPropertyName("user_deck_my_rotation")] [Key("user_deck_my_rotation")] public UserFormatDeckInfo UserMyRotationDecks { get; set; } = new(); [JsonPropertyName("user_card_list")] [Key("user_card_list")] public List UserCards { get; set; } = new(); [JsonPropertyName("user_class_list")] [Key("user_class_list")] public List UserClasses { get; set; } = new(); /// /// Wire is an array; parser iterates by index (LoadDetail.cs:358-360). /// [JsonPropertyName("user_sleeve_list")] [Key("user_sleeve_list")] public List Sleeves { get; set; } = new(); [JsonPropertyName("user_emblem_list")] [Key("user_emblem_list")] public List UserEmblems { get; set; } = new(); [JsonPropertyName("user_degree_list")] [Key("user_degree_list")] public List UserDegrees { get; set; } = new(); /// /// Wire is an array; parser iterates by index (LoadDetail.cs:348-356). /// [JsonPropertyName("user_leader_skin_list")] [Key("user_leader_skin_list")] public List LeaderSkins { get; set; } = new(); /// /// Wire is string[]; parser calls .ToString() on each element (LoadDetail.cs:387-392). /// [JsonPropertyName("user_mypage_list")] [Key("user_mypage_list")] public List MyPageBackgrounds { get; set; } = new(); #endregion #region Advanced Player Data /// /// Wire is an array of 5 entries; parser uses deck_format as discriminator /// (LoadDetail.cs:527-538). /// [JsonPropertyName("user_rank")] [Key("user_rank")] public List UserRankInfo { get; set; } = new(); [JsonPropertyName("user_rank_match_list")] [Key("user_rank_match_list")] public List UserRankedMatches { get; set; } = new(); /// /// Spec: optional. Shape is {normal?, total?, campaign?[]} per common/types.ts.md DailyLoginBonus. /// Until we have an active login-bonus campaign to surface in spec shape, omit. The skeleton /// rows in DailyLoginBonuses table (prod sent {"1":[], "3":[], "4":[]}) preserve the capture /// for archive but don't make it into the wire response. /// [JsonPropertyName("daily_login_bonus")] [Key("daily_login_bonus")] public DailyLoginBonus? DailyLoginBonus { get; set; } [JsonPropertyName("challenge_config")] [Key("challenge_config")] public ArenaConfig ArenaConfig { get; set; } = new(); #endregion #region Global Data [JsonPropertyName("red_ether_overwrite_list")] [Key("red_ether_overwrite_list")] public List RedEtherOverrides { get; set; } = new(); /// /// Wire is a flat number[]; parser passes it straight to SetMaintenanceCardIds /// (LoadDetail.cs:165). /// [JsonPropertyName("maintenance_card_list")] [Key("maintenance_card_list")] public List MaintenanceCards { get; set; } = new(); /// /// Client's ArenaData(JsonData) ctor reads data[0] unconditionally inside the /// LoadDetail.cs:261 Keys.Contains("arena_info") branch — an empty list crashes /// /load/index with ArgumentOutOfRangeException. Send null (omitted on wire) when /// there's no arena to advertise. /// [JsonPropertyName("arena_info")] [Key("arena_info")] public List? ArenaInfos { get; set; } /// /// Wire is an array; client uses POSITIONAL logic (index >= 24 = master ranks, /// LoadDetail.cs:417-422). Order must match repository's ordering. /// [JsonPropertyName("rank_info")] [Key("rank_info")] public List RankInfo { get; set; } = new(); /// /// Wire is an array; parser iterates by index (LoadDetail.cs:425-434). /// [JsonPropertyName("class_exp")] [Key("class_exp")] public List ClassExp { get; set; } = new(); [JsonPropertyName("loading_exclusion_card_list")] [Key("loading_exclusion_card_list")] public List LoadingTipCardExclusions { get; set; } = new(); [JsonPropertyName("default_setting")] [Key("default_setting")] public DefaultSettings DefaultSettings { get; set; } = new(); [JsonPropertyName("unlimited_restricted_base_card_id_list")] [Key("unlimited_restricted_base_card_id_list")] public Dictionary UnlimitedBanList { get; set; } = new(); /// /// Client unconditionally accesses [1] and [Count-1] (LoadDetail.cs:184) 窶・list MUST /// have at least 2 entries or the client crashes. /// [JsonPropertyName("rotation_card_set_id_list")] [Key("rotation_card_set_id_list")] public List RotationSets { get; set; } = new(); /// /// Wire is a flat number[]; parser iterates and reads .ToInt() (LoadDetail.cs:463-468). /// [JsonPropertyName("reprinted_base_card_ids")] [Key("reprinted_base_card_ids")] public List ReprintedCards { get; set; } = new(); [JsonPropertyName("spot_cards")] [Key("spot_cards")] public Dictionary SpotCards { get; set; } = new(); [JsonPropertyName("pre_release_info")] [Key("pre_release_info")] public PreReleaseInfo? PreReleaseInfo { get; set; } [JsonPropertyName("my_rotation_info")] [Key("my_rotation_info")] public MyRotationInfo? MyRotationInfo { get; set; } [JsonPropertyName("avatar_info")] [Key("avatar_info")] public AvatarInfo? AvatarRotationInfo { get; set; } [JsonPropertyName("feature_maintenance_list")] [Key("feature_maintenance_list")] public List FeatureMaintenances { get; set; } = new(); [JsonPropertyName("special_crystal_info")] [Key("special_crystal_info")] public List SpecialCrystalInfos { get; set; } = new(); /// /// Spec: optional, Record<string, BattlePassLevelInfo> keyed by level-as-string /// (load-index.md:228). Omit (null) when no Battle Pass is active. /// [JsonPropertyName("battle_pass_level_info")] [Key("battle_pass_level_info")] public IReadOnlyDictionary? BattlePassLevelInfo { get; set; } /// /// Wire is string[]; parser calls .ToString() on each element (LoadDetail.cs:493-499). /// [JsonPropertyName("open_battle_field_id_list")] [Key("open_battle_field_id_list")] public List OpenBattlefieldIds { get; set; } = new(); #endregion #region Misc Data [JsonPropertyName("loot_box_regulation")] [Key("loot_box_regulation")] public LootBoxRegulations LootBoxRegulations { get; set; } = new(); [JsonPropertyName("gathering_info")] [Key("gathering_info")] public GatheringInfo GatheringInfo { get; set; } = new(); /// /// Spec is unclear whether this is returned at /load/index or only at /config/* endpoints /// (load-index.md line 390). Pending live-capture confirmation; harmless extra. /// [JsonPropertyName("user_config")] [Key("user_config")] public UserConfig UserConfig { get; set; } = new(); #endregion }