using System;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Vanguard.Api;
using Vanguard.Api.Contracts;
using Vanguard.Auth;
using Vanguard.Meta;
namespace Vanguard.UI
{
/// Первый вход: выбор одной из двух бесплатных триал-дек.
public sealed class OnboardingView
{
private readonly AuthService _auth;
private readonly MetaService _meta;
private readonly Action _onCompleted;
private readonly Action _onLoggedOut;
private readonly RectTransform _root;
private readonly RectTransform _deckRow;
private readonly Text _status;
private readonly Button _retry;
private readonly Button _logout;
private bool _loading;
public OnboardingView(
Transform parent,
AuthService auth,
MetaService meta,
Action onCompleted,
Action onLoggedOut)
{
_auth = auth;
_meta = meta;
_onCompleted = onCompleted;
_onLoggedOut = onLoggedOut;
_root = UiKit.CreatePanel(parent, "OnboardingScreen", new Color(0.035f, 0.045f, 0.065f, 1f));
var card = UiKit.CreateCenteredCard(
_root,
"OnboardingCard",
new Vector2(1320f, 900f),
new Color(0.07f, 0.085f, 0.115f, 1f));
UiKit.AddVerticalLayout(card, 34, 12);
var title = UiKit.CreateText(
card, "Title", "Стартовый набор", 46, Color.white);
title.fontStyle = FontStyle.Bold;
UiKit.SetPreferredHeight(title, 58f);
var subtitle = UiKit.CreateText(
card,
"Subtitle",
"Выберите одну триал-деку. 50 карт попадут в коллекцию, плюс 5 токенов на бустеры.",
20,
new Color(0.68f, 0.74f, 0.88f));
UiKit.SetPreferredHeight(subtitle, 54f);
_deckRow = UiKit.CreatePanel(
card,
"DeckRow",
new Color(0.05f, 0.06f, 0.085f, 1f));
UiKit.SetPreferredHeight(_deckRow, 500f);
UiKit.AddHorizontalLayout(_deckRow, 16, 18);
_status = UiKit.CreateText(
card, "Status", string.Empty, 18,
new Color(0.68f, 0.74f, 0.88f),
TextAnchor.MiddleCenter);
UiKit.SetPreferredHeight(_status, 38f);
_retry = UiKit.CreateButton(card, "Retry", "Повторить загрузку", OnRetryClicked, 18);
UiKit.SetPreferredHeight(_retry, 42f);
_retry.gameObject.SetActive(false);
_logout = UiKit.CreateButton(card, "Logout", "Выйти из аккаунта", OnLogoutClicked, 18);
UiKit.SetPreferredHeight(_logout, 42f);
_root.gameObject.SetActive(false);
}
public void Show()
{
UiKit.SetActive(_root, true);
_ = LoadAsync();
}
public void Hide() => UiKit.SetActive(_root, false);
private async Task LoadAsync()
{
if (_loading)
return;
_loading = true;
_retry.gameObject.SetActive(false);
_logout.interactable = false;
ClearDeckRow();
SetStatus("Загружаем триал-деки…", new Color(0.68f, 0.74f, 0.88f));
try
{
var store = await _meta.GetStoreAsync();
BuildDecks(store);
SetStatus("Выберите деку — выбор делается один раз.", new Color(0.68f, 0.74f, 0.88f));
}
catch (ApiException exception)
{
SetStatus(exception.Message, new Color(1f, 0.55f, 0.45f));
_retry.gameObject.SetActive(true);
}
catch (Exception exception)
{
Debug.LogException(exception);
SetStatus("Не удалось загрузить триал-деки. Сервер недоступен?", new Color(1f, 0.55f, 0.45f));
_retry.gameObject.SetActive(true);
}
finally
{
_loading = false;
_logout.interactable = true;
}
}
private void BuildDecks(StoreResponse store)
{
if (store.TrialDecks == null || store.TrialDecks.Count == 0)
{
SetStatus("Сервер не вернул ни одной триал-деки.", new Color(1f, 0.55f, 0.45f));
return;
}
foreach (var deck in store.TrialDecks)
BuildDeck(deck);
}
private void BuildDeck(StoreTrialDeckDto deck)
{
var card = UiKit.CreatePanel(
_deckRow,
"Trial_" + deck.Id,
new Color(0.09f, 0.11f, 0.15f, 1f));
var element = card.gameObject.AddComponent();
element.flexibleWidth = 1f;
element.preferredHeight = 460f;
UiKit.AddVerticalLayout(card, 24, 10);
var name = UiKit.CreateText(card, "Name", deck.Name, 30, Color.white, TextAnchor.MiddleLeft);
UiKit.MakeLiteral(name);
name.fontStyle = FontStyle.Bold;
UiKit.SetPreferredHeight(name, 42f);
var clan = UiKit.CreateText(
card, "Clan", "Клан: " + (string.IsNullOrEmpty(deck.Clan) ? "—" : deck.Clan),
20, new Color(0.68f, 0.74f, 0.88f), TextAnchor.MiddleLeft);
UiKit.SetPreferredHeight(clan, 30f);
var count = UiKit.CreateText(
card, "Count", deck.CardCount + " карт · " + deck.Cards.Count + " уникальных",
18, new Color(0.60f, 0.67f, 0.80f), TextAnchor.MiddleLeft);
UiKit.SetPreferredHeight(count, 28f);
var preview = UiKit.CreateText(
card,
"Preview",
BuildPreview(deck),
16,
new Color(0.52f, 0.60f, 0.74f),
TextAnchor.UpperLeft);
preview.horizontalOverflow = HorizontalWrapMode.Wrap;
UiKit.SetPreferredHeight(preview, 150f, 1f);
if (deck.Owned)
{
var owned = UiKit.CreateText(
card, "Owned", "Уже в коллекции", 18,
new Color(0.45f, 0.85f, 0.55f));
UiKit.SetPreferredHeight(owned, 28f);
}
var choose = UiKit.CreateButton(
card,
"Choose",
deck.Owned ? "Дека уже получена" : "Выбрать бесплатно",
() => OnChooseClicked(deck),
21);
choose.interactable = !deck.Owned;
UiKit.SetPreferredHeight(choose, 54f);
}
private static string BuildPreview(StoreTrialDeckDto deck)
{
if (deck.Cards == null || deck.Cards.Count == 0)
return "Состав появится после выбора.";
var lines = deck.Cards
.Take(8)
.Select(card => "• " + card.PrintingId + " ×" + card.Count);
return string.Join("\n", lines);
}
private async void OnChooseClicked(StoreTrialDeckDto deck)
{
if (_loading)
return;
_loading = true;
_retry.gameObject.SetActive(false);
SetStatus("Активируем " + deck.Name + "…", new Color(0.68f, 0.74f, 0.88f));
try
{
await _meta.OnboardAsync(deck.Id);
_onCompleted();
}
catch (ApiException exception)
{
SetStatus(exception.Message, new Color(1f, 0.55f, 0.45f));
}
catch (Exception exception)
{
Debug.LogException(exception);
SetStatus("Не удалось завершить онбординг.", new Color(1f, 0.55f, 0.45f));
}
finally
{
_loading = false;
}
}
private void OnRetryClicked()
{
_ = LoadAsync();
}
private async void OnLogoutClicked()
{
_logout.interactable = false;
await _auth.LogoutAsync();
_onLoggedOut();
}
private void ClearDeckRow()
{
for (var i = _deckRow.childCount - 1; i >= 0; i--)
UnityEngine.Object.Destroy(_deckRow.GetChild(i).gameObject);
}
private void SetStatus(string text, Color color)
{
_status.text = text ?? string.Empty;
_status.color = color;
}
}
}