using System.Text.Json; using SVSim.BattleNode.Bridge; using SVSim.Database.Enums; using SVSim.Database.Models.Config; using SVSim.Database.Repositories.Deck; using SVSim.Database.Repositories.Viewer; using SVSim.Database.Services; namespace SVSim.EmulatedEntrypoint.Services; public class MatchContextBuilder : IMatchContextBuilder { private readonly IArenaTwoPickRunRepository _runs; private readonly IViewerRepository _viewers; private readonly IDeckRepository _decks; private readonly IGameConfigService _config; public MatchContextBuilder( IArenaTwoPickRunRepository runs, IViewerRepository viewers, IDeckRepository decks, IGameConfigService config) { _runs = runs; _viewers = viewers; _decks = decks; _config = config; } public async Task BuildForTwoPickAsync(long viewerId) { var run = await _runs.GetByViewerIdAsync(viewerId) ?? throw new ArenaTwoPickException("arena_two_pick_no_active_run"); var deck = JsonSerializer.Deserialize>(run.SelectedCardIdsJson) ?? new(); if (deck.Count < 30) throw new ArenaTwoPickException("arena_two_pick_draft_incomplete"); var viewer = await _viewers.LoadForMatchContextAsync(viewerId) ?? throw new ArenaTwoPickException("arena_two_pick_no_active_run"); var challenge = _config.Get(); var defaults = _config.Get(); var emblemId = viewer.Info.SelectedEmblem.Id != 0 ? viewer.Info.SelectedEmblem.Id.ToString() : defaults.EmblemId.ToString(); var degreeId = viewer.Info.SelectedDegree.Id != 0 ? viewer.Info.SelectedDegree.Id.ToString() : defaults.DegreeId.ToString(); var charaId = run.LeaderSkinId != 0 ? run.LeaderSkinId.ToString() : run.ClassId.ToString(); return new MatchContext( SelfDeckCardIds: deck, ClassId: run.ClassId.ToString(), CharaId: charaId, // Hardcoded v1; see spec §Deferred plumbing. CardMasterName: "card_master_node_10015", CountryCode: viewer.Info.CountryCode ?? string.Empty, UserName: viewer.DisplayName, // TK2-specific cosmetic source; other modes will use the deck row's SleeveId. SleeveId: challenge.TwoPickSleeveId.ToString(), EmblemId: emblemId, DegreeId: degreeId, // Hardcoded v1; needs equipped-MyPageBackground lookup (see spec §Deferred). FieldId: 43, IsOfficial: viewer.Info.IsOfficial ? 1 : 0, BattleType: 11); } public async Task BuildForRankBattleAsync(long viewerId, Format format) { var viewer = await _viewers.LoadForMatchContextAsync(viewerId) ?? throw new InvalidOperationException($"viewer {viewerId} not found"); // Per spec, deck-selection persistence (which deck number is "current" for this // format) is a separate concern; #1 is a placeholder until that lands. IDeckRepository // is the right path here — viewer-graph nav refs (DeckCard.Card) don't auto-load // (see project_ef_nav_include_pitfall memory), which would silently ship card_id=0. var deck = await _decks.GetDeck(viewerId, format, deckNo: 1) ?? throw new InvalidOperationException($"viewer {viewerId} has no deck for format {format}"); var defaults = _config.Get(); var emblemId = viewer.Info.SelectedEmblem.Id != 0 ? viewer.Info.SelectedEmblem.Id.ToString() : defaults.EmblemId.ToString(); var degreeId = viewer.Info.SelectedDegree.Id != 0 ? viewer.Info.SelectedDegree.Id.ToString() : defaults.DegreeId.ToString(); var charaId = deck.LeaderSkin.Id != 0 ? deck.LeaderSkin.Id.ToString() : deck.Class.Id.ToString(); var sleeveId = deck.Sleeve.Id != 0 ? deck.Sleeve.Id.ToString() : defaults.SleeveId.ToString(); var deckCardIds = deck.Cards.Select(c => c.Card.Id).ToList(); return new MatchContext( SelfDeckCardIds: deckCardIds, ClassId: deck.Class.Id.ToString(), CharaId: charaId, CardMasterName: "card_master_node_10015", CountryCode: viewer.Info.CountryCode ?? string.Empty, UserName: viewer.DisplayName, SleeveId: sleeveId, EmblemId: emblemId, DegreeId: degreeId, FieldId: 43, IsOfficial: viewer.Info.IsOfficial ? 1 : 0, BattleType: 11); } }