using System;
using System.Collections.Generic;
using System.IO;
using TMPro;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;

namespace MajacloudSdk.Editor
{
    /// <summary>
    /// Builds the demo scenes and prefabs in code so the sample never depends on
    /// hand-edited YAML, works in any render pipeline and with either input system.
    /// One Home scene links to an All-in-One scene, one scene per service, a Test
    /// Data playground and a short Docs scene.
    /// </summary>
    internal static class StarterSampleBuilder
    {
        private static readonly Vector2 Reference = new Vector2(1080, 1920);

        private sealed class Prefabs
        {
            public GameObject LeaderboardView;
            public GameObject PlayerBadge;
            public GameObject DailyRewardButton;
            public GameObject ConfigRow;
        }

        private sealed class Shell
        {
            public Component Controller;
            public Transform Body;
        }

        private struct Field
        {
            public string Name;
            public string Caption;
            public string Value;
            public string Placeholder;
            public float Width;
            public TMP_InputField.ContentType Type;

            public Field(string name, string caption, string value, string placeholder, float width = -1, TMP_InputField.ContentType type = TMP_InputField.ContentType.Standard)
            {
                Name = name;
                Caption = caption;
                Value = value;
                Placeholder = placeholder;
                Width = width;
                Type = type;
            }
        }

        private enum Style { Primary, Secondary, Danger }

        public static void Build(bool interactive, bool confirm = true)
        {
            if (interactive)
            {
                if (confirm && File.Exists(MajacloudEditorPaths.HomeScenePath) && !EditorUtility.DisplayDialog("Rebuild demo scenes?", "This replaces the demo scenes and prefabs under " + MajacloudEditorPaths.SamplesRoot + ".", "Rebuild", "Cancel")) return;
                if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
                TmpResources.Ensure(() =>
                {
                    BuildNow();
                    EditorSceneManager.OpenScene(MajacloudEditorPaths.HomeScenePath);
                });
                return;
            }
            TmpResources.Ensure(BuildNow);
        }

        private static void BuildNow()
        {
            MajacloudEditorPaths.EnsureFolder(MajacloudEditorPaths.ScenesPath);
            MajacloudEditorPaths.EnsureFolder(MajacloudEditorPaths.PrefabsPath);
            SoftArt.Ensure();

            var rowPrefab = BuildLeaderboardRowPrefab();
            var prefabs = new Prefabs
            {
                ConfigRow = BuildConfigRowPrefab(),
                LeaderboardView = BuildLeaderboardViewPrefab(rowPrefab),
                PlayerBadge = BuildPlayerBadgePrefab(),
                DailyRewardButton = BuildDailyRewardButtonPrefab(),
            };

            BuildScene(DemoScenes.Home, () => BuildHome(prefabs));
            BuildScene(DemoScenes.AllInOne, () => BuildAllInOne(prefabs));
            BuildScene(DemoScenes.SignIn, () => BuildSignIn(prefabs));
            BuildScene(DemoScenes.Leaderboard, () => BuildLeaderboard(prefabs));
            BuildScene(DemoScenes.CloudSave, BuildCloudSave);
            BuildScene(DemoScenes.Rewards, () => BuildRewards(prefabs));
            BuildScene(DemoScenes.RemoteConfig, () => BuildRemoteConfig(prefabs));
            BuildScene(DemoScenes.TestData, BuildTestData);
            BuildScene(DemoScenes.Docs, BuildDocs);

            if (File.Exists(MajacloudEditorPaths.LegacyStarterScenePath)) AssetDatabase.DeleteAsset(MajacloudEditorPaths.LegacyStarterScenePath);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            UpdateBuildSettings();
            Debug.Log("[Majacloud] Demo scenes built in " + MajacloudEditorPaths.ScenesPath + ". Open " + DemoScenes.Home + " and press Play.");
        }

        /// <summary>
        /// The shipped scenes are saved with Unity 6. TextMesh Pro before Unity 2023.2 stores text
        /// wrapping in another field, so on those versions the scenes are rebuilt locally instead.
        /// </summary>
        public static bool ScenesNeedRebuildForThisUnity()
        {
            var path = MajacloudEditorPaths.HomeScenePath;
            return IsUnityOlderThan(2023, 2) && File.Exists(path) && File.ReadAllText(path).Contains("m_TextWrappingMode");
        }

        private static bool IsUnityOlderThan(int major, int minor)
        {
            var parts = Application.unityVersion.Split('.');
            if (parts.Length < 2 || !int.TryParse(parts[0], out var currentMajor) || !int.TryParse(parts[1], out var currentMinor)) return false;
            return currentMajor < major || (currentMajor == major && currentMinor < minor);
        }

        private static void BuildScene(string fileName, Action build)
        {
            var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
            build();
            EditorSceneManager.SaveScene(scene, MajacloudEditorPaths.ScenePath(fileName));
        }

        /// <summary>Home first, then every demo; other scenes in the project keep their place after them.</summary>
        private static void UpdateBuildSettings()
        {
            var demoPaths = new List<string> { MajacloudEditorPaths.HomeScenePath };
            foreach (var demo in DemoScenes.Demos) demoPaths.Add(MajacloudEditorPaths.ScenePath(demo.File));
            var scenes = new List<EditorBuildSettingsScene>();
            foreach (var path in demoPaths) scenes.Add(new EditorBuildSettingsScene(path, true));
            foreach (var scene in EditorBuildSettings.scenes)
            {
                if (demoPaths.Contains(scene.path) || scene.path == MajacloudEditorPaths.LegacyStarterScenePath) continue;
                scenes.Add(scene);
            }
            EditorBuildSettings.scenes = scenes.ToArray();
        }

        // --- Prefabs ---------------------------------------------------------------------

        private static GameObject BuildLeaderboardRowPrefab()
        {
            var root = UiBuilder.CreatePanel("MajacloudLeaderboardRow", null, UiTheme.Row, UiTheme.RadiusControl).gameObject;
            var background = root.GetComponent<Image>();
            UiBuilder.AddHorizontal(root, 16, 20);
            UiBuilder.SetSize(root, 88);
            var rank = UiBuilder.CreateText("Rank", root.transform, "1", UiTheme.Body, UiTheme.Muted, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(rank.gameObject, -1, 110);
            var name = UiBuilder.CreateText("Name", root.transform, "Player", UiTheme.Body, UiTheme.Text, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(name.gameObject, -1, -1, 1);
            var score = UiBuilder.CreateText("Score", root.transform, "0", UiTheme.Body, UiTheme.Text, TextAlignmentOptions.MidlineRight, FontStyles.Bold);
            UiBuilder.SetSize(score.gameObject, -1, 260);
            var view = UiBuilder.AddSampleComponent(root, "LeaderboardRowView");
            UiBuilder.SetField(view, "rankLabel", rank);
            UiBuilder.SetField(view, "nameLabel", name);
            UiBuilder.SetField(view, "scoreLabel", score);
            UiBuilder.SetField(view, "background", background);
            return SavePrefab(root, "MajacloudLeaderboardRow");
        }

        private static GameObject BuildConfigRowPrefab()
        {
            var root = UiBuilder.CreatePanel("MajacloudConfigRow", null, UiTheme.Row, UiTheme.RadiusControl).gameObject;
            UiBuilder.AddHorizontal(root, 16, 20);
            UiBuilder.SetSize(root, 88);
            var key = UiBuilder.CreateText("Key", root.transform, "key", UiTheme.Small, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(key.gameObject, -1, 340);
            var type = UiBuilder.CreateText("Type", root.transform, "string", UiTheme.Tiny, UiTheme.AccentInk, TextAlignmentOptions.Midline);
            UiBuilder.SetSize(type.gameObject, -1, 140);
            var value = UiBuilder.CreateText("Value", root.transform, "value", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.MidlineRight);
            UiBuilder.SetSize(value.gameObject, -1, -1, 1);
            var view = UiBuilder.AddSampleComponent(root, "ConfigRowView");
            UiBuilder.SetField(view, "keyLabel", key);
            UiBuilder.SetField(view, "typeLabel", type);
            UiBuilder.SetField(view, "valueLabel", value);
            return SavePrefab(root, "MajacloudConfigRow");
        }

        private static GameObject BuildLeaderboardViewPrefab(GameObject rowPrefab)
        {
            var root = UiBuilder.CreatePanel("MajacloudLeaderboardView", null, UiTheme.Panel).gameObject;
            UiBuilder.AddVertical(root, 12, 28);
            UiBuilder.SetSize(root, 900);

            var header = UiBuilder.CreateRect("Header", root.transform);
            UiBuilder.AddHorizontal(header, 12, 0);
            UiBuilder.SetSize(header, 64);
            var title = UiBuilder.CreateText("Title", header.transform, "Leaderboard", UiTheme.Heading, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(title.gameObject, -1, -1, 1);
            var slugPill = UiBuilder.CreatePanel("SlugPill", header.transform, UiTheme.AccentSoft, UiTheme.Pill(64));
            UiBuilder.SetSize(slugPill.gameObject, -1, 150);
            var slug = UiBuilder.CreateText("Slug", slugPill.transform, "coins", UiTheme.Tiny, UiTheme.AccentInk, TextAlignmentOptions.Center, FontStyles.Bold);
            UiBuilder.Stretch(slug.rectTransform, 12, 4);
            var refresh = UiBuilder.CreateButton("Refresh", header.transform, "Refresh", UiTheme.Panel2, UiTheme.Text, UiTheme.Small);
            UiBuilder.SetSize(refresh.gameObject, 64, 170);

            var columns = UiBuilder.CreateRect("Columns", root.transform);
            UiBuilder.AddHorizontal(columns, 16, 0).padding = new RectOffset(20, 20, 0, 0);
            UiBuilder.SetSize(columns, 40);
            var c1 = UiBuilder.CreateText("Rank", columns.transform, "RANK", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(c1.gameObject, -1, 110);
            var c2 = UiBuilder.CreateText("Player", columns.transform, "PLAYER", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(c2.gameObject, -1, -1, 1);
            var c3 = UiBuilder.CreateText("Score", columns.transform, "SCORE", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineRight);
            UiBuilder.SetSize(c3.gameObject, -1, 260);

            var (scroll, content) = UiBuilder.CreateScrollList("List", root.transform, 8);
            UiBuilder.SetSize(scroll.gameObject, -1, -1, -1, 1);

            var myRank = UiBuilder.CreateText("MyRank", root.transform, "Sign in to see your rank", UiTheme.Small, UiTheme.AccentInk, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(myRank.gameObject, 44);
            var status = UiBuilder.CreateText("Status", root.transform, string.Empty, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(status.gameObject, 36);

            var view = UiBuilder.AddSampleComponent(root, "LeaderboardView");
            UiBuilder.SetField(view, "titleLabel", title);
            UiBuilder.SetField(view, "slugLabel", slug);
            UiBuilder.SetField(view, "refreshButton", refresh);
            UiBuilder.SetField(view, "content", content);
            UiBuilder.SetField(view, "rowPrefab", SampleComponent(rowPrefab, "LeaderboardRowView"));
            UiBuilder.SetField(view, "myRankLabel", myRank);
            UiBuilder.SetField(view, "statusLabel", status);
            return SavePrefab(root, "MajacloudLeaderboardView");
        }

        private static GameObject BuildPlayerBadgePrefab()
        {
            var root = UiBuilder.CreatePanel("MajacloudPlayerBadge", null, UiTheme.Panel).gameObject;
            UiBuilder.AddHorizontal(root, 16, 16);
            UiBuilder.SetSize(root, 104, 520);
            var avatar = UiBuilder.CreatePanel("Avatar", root.transform, UiTheme.AccentSoft, UiTheme.Pill(72));
            UiBuilder.SetSize(avatar.gameObject, 72, 72);
            var initial = UiBuilder.CreateText("Initial", avatar.transform, "G", UiTheme.Small, UiTheme.AccentInk, TextAlignmentOptions.Center, FontStyles.Bold);
            UiBuilder.Stretch(initial.rectTransform, 0, 0);
            var column = UiBuilder.CreateRect("Column", root.transform);
            UiBuilder.AddVertical(column, 0, 0, TextAnchor.MiddleLeft);
            UiBuilder.SetSize(column, -1, -1, 1);
            var name = UiBuilder.CreateText("Name", column.transform, "Not signed in", UiTheme.Small, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            var auth = UiBuilder.CreateText("Auth", column.transform, "guest", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            var coinsPill = UiBuilder.CreatePanel("CoinsPill", root.transform, UiTheme.AccentSoft, UiTheme.Pill(72));
            UiBuilder.SetSize(coinsPill.gameObject, -1, 200);
            var coins = UiBuilder.CreateText("Coins", coinsPill.transform, "0 coins", UiTheme.Small, UiTheme.AccentInk, TextAlignmentOptions.Center, FontStyles.Bold);
            UiBuilder.Stretch(coins.rectTransform, 12, 4);
            var badge = UiBuilder.AddSampleComponent(root, "PlayerBadge");
            UiBuilder.SetField(badge, "nameLabel", name);
            UiBuilder.SetField(badge, "authLabel", auth);
            UiBuilder.SetField(badge, "coinsLabel", coins);
            UiBuilder.SetField(badge, "initialLabel", initial);
            return SavePrefab(root, "MajacloudPlayerBadge");
        }

        private static GameObject BuildDailyRewardButtonPrefab()
        {
            var button = UiBuilder.CreateButton("MajacloudDailyRewardButton", null, "Claim daily reward", UiTheme.Accent, UiTheme.OnAccent, UiTheme.Body, UiTheme.Pill(116));
            var root = button.gameObject;
            UiBuilder.SetSize(root, 116);
            var label = root.GetComponentInChildren<TextMeshProUGUI>();
            label.rectTransform.offsetMin = new Vector2(24, 26);
            var countdown = UiBuilder.CreateText("Countdown", root.transform, string.Empty, UiTheme.Tiny, UiTheme.OnAccent, TextAlignmentOptions.Bottom);
            UiBuilder.Stretch(countdown.rectTransform, 24, 8);
            var component = UiBuilder.AddSampleComponent(root, "DailyRewardButton");
            UiBuilder.SetField(component, "button", button);
            UiBuilder.SetField(component, "label", label);
            UiBuilder.SetField(component, "countdownLabel", countdown);
            return SavePrefab(root, "MajacloudDailyRewardButton");
        }

        private static GameObject SavePrefab(GameObject root, string name)
        {
            var prefab = UiBuilder.SavePrefab(root, MajacloudEditorPaths.PrefabsPath + "/" + name + ".prefab");
            Object.DestroyImmediate(root);
            return prefab;
        }

        // --- Scenes -----------------------------------------------------------------------

        private static void BuildHome(Prefabs prefabs)
        {
            var shell = CreateShell(null, null, scroll: true);
            var body = shell.Body;

            var hero = UiBuilder.CreateRect("Hero", body);
            var heroLayout = UiBuilder.AddVertical(hero, 6, 0, TextAnchor.MiddleCenter);
            heroLayout.padding = new RectOffset(0, 0, 24, 12);
            var logo = UiBuilder.CreateImage("Logo", hero.transform, SoftArt.Logo);
            UiBuilder.SetSize(logo.gameObject, 150);
            var title = UiBuilder.CreateText("Title", hero.transform, "Majacloud", 60, UiTheme.Text, TextAlignmentOptions.Center, FontStyles.Bold);
            UiBuilder.SetSize(title.gameObject, 76);
            var tagline = UiBuilder.CreateText("Tagline", hero.transform, "Unity SDK " + Majacloud.Version + " · Demo scenes", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.Center);
            UiBuilder.SetSize(tagline.gameObject, 40);

            var connection = UiBuilder.CreatePanel("Connection", body, UiTheme.Panel);
            UiBuilder.AddHorizontal(connection.gameObject, 16, 28);
            UiBuilder.SetSize(connection.gameObject, 96);
            var connectionLabel = UiBuilder.CreateText("Label", connection.transform, "Checking connection…", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(connectionLabel.gameObject, -1, -1, 1);
            UiBuilder.SetField(shell.Controller, "connectionLabel", connectionLabel);

            var badge = InstantiatePrefab(prefabs.PlayerBadge, body);
            UiBuilder.SetField(shell.Controller, "badge", SampleComponent(badge, "PlayerBadge"));

            var label = UiBuilder.CreateText("DemosLabel", body, "DEMOS", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.BottomLeft, FontStyles.Bold);
            UiBuilder.SetSize(label.gameObject, 44);
            foreach (var demo in DemoScenes.Demos) CreateDemoRow(body, demo);

            var footer = UiBuilder.CreateText("Footer", body, "Every scene also works on its own. Set up first: Tools › Majacloud › Setup.", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.Center);
            UiBuilder.SetSize(footer.gameObject, 80);
        }

        private static void CreateDemoRow(Transform parent, DemoScenes.Entry demo)
        {
            var button = UiBuilder.CreateButton(demo.Title, parent, null, UiTheme.Panel, UiTheme.Text, UiTheme.Body, UiTheme.RadiusCard);
            var row = button.gameObject;
            UiBuilder.AddHorizontal(row, 24, 24);
            UiBuilder.SetSize(row, 136);
            var icon = UiBuilder.CreatePanel("Icon", row.transform, UiTheme.AccentSoft, UiTheme.Pill(88));
            UiBuilder.SetSize(icon.gameObject, -1, 88);
            var letter = UiBuilder.CreateText("Letter", icon.transform, demo.Icon, UiTheme.Heading, UiTheme.AccentInk, TextAlignmentOptions.Center, FontStyles.Bold);
            UiBuilder.Stretch(letter.rectTransform, 0, 0);
            var texts = UiBuilder.CreateRect("Texts", row.transform);
            UiBuilder.AddVertical(texts, 2, 0, TextAnchor.MiddleLeft);
            UiBuilder.SetSize(texts, -1, -1, 1);
            var title = UiBuilder.CreateText("Title", texts.transform, demo.Title, UiTheme.Body, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(title.gameObject, 44);
            var summary = UiBuilder.CreateText("Summary", texts.transform, demo.Summary, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(summary.gameObject, 34);
            var chevron = UiBuilder.CreateText("Chevron", row.transform, "›", UiTheme.Title, UiTheme.Muted, TextAlignmentOptions.Center);
            UiBuilder.SetSize(chevron.gameObject, -1, 40);
            AddSceneLink(row, MajacloudEditorPaths.ScenePath(demo.File));
        }

        private static void BuildAllInOne(Prefabs prefabs)
        {
            var shell = CreateShell("All-in-One", "Every service in one screen");
            var badge = InstantiatePrefab(prefabs.PlayerBadge, shell.Body);

            var tabBarGo = UiBuilder.CreatePanel("TabBar", shell.Body, UiTheme.Panel, UiTheme.Pill(96)).gameObject;
            UiBuilder.AddHorizontal(tabBarGo, 6, 10, TextAnchor.MiddleCenter);
            UiBuilder.SetSize(tabBarGo, 96);
            var tabNames = new[] { "Scores", "Save", "Rewards", "Config", "Account" };
            var tabButtons = new List<Button>();
            foreach (var tabName in tabNames)
            {
                var tab = UiBuilder.CreateButton(tabName, tabBarGo.transform, tabName, UiTheme.Panel, UiTheme.Muted, UiTheme.Tiny, UiTheme.Pill(76));
                UiBuilder.SetSize(tab.gameObject, -1, -1, 1);
                tabButtons.Add(tab);
            }

            var pages = UiBuilder.CreateRect("Pages", shell.Body);
            UiBuilder.SetSize(pages, -1, -1, -1, 1);
            var leaderboardPage = CreatePage("LeaderboardPage", pages.transform);
            var cloudPage = CreatePage("CloudSavePage", pages.transform);
            var rewardsPage = CreatePage("RewardsPage", pages.transform);
            var configPage = CreatePage("ConfigPage", pages.transform);
            var accountPage = CreatePage("AccountPage", pages.transform);

            var leaderboardPanel = BuildLeaderboardSection(leaderboardPage.transform, prefabs);
            var cloudPanel = BuildCloudSaveSection(cloudPage.transform);
            var rewardsPanel = BuildRewardsSection(rewardsPage.transform, prefabs);
            var configPanel = BuildConfigSection(configPage.transform, prefabs);
            var accountPanel = BuildAccountSection(accountPage.transform);

            var pageObjects = new[] { leaderboardPage, cloudPage, rewardsPage, configPage, accountPage };
            var tabBar = UiBuilder.AddSampleComponent(tabBarGo, "TabBar");
            UiBuilder.SetArray(tabBar, "buttons", tabButtons.ToArray());
            UiBuilder.SetArray(tabBar, "pages", pageObjects);
            for (int i = 1; i < pageObjects.Length; i++) pageObjects[i].SetActive(false);

            UiBuilder.SetField(shell.Controller, "tabBar", tabBar);
            UiBuilder.SetField(shell.Controller, "badge", SampleComponent(badge, "PlayerBadge"));
            UiBuilder.SetField(shell.Controller, "leaderboardPanel", leaderboardPanel);
            UiBuilder.SetField(shell.Controller, "cloudSavePanel", cloudPanel);
            UiBuilder.SetField(shell.Controller, "rewardsPanel", rewardsPanel);
            UiBuilder.SetField(shell.Controller, "configPanel", configPanel);
            UiBuilder.SetField(shell.Controller, "accountPanel", accountPanel);
        }

        private static void BuildSignIn(Prefabs prefabs)
        {
            var shell = CreateShell("Sign-in", "Guest player, name and account linking", scroll: true);
            var badge = InstantiatePrefab(prefabs.PlayerBadge, shell.Body);
            var panel = BuildAccountSection(shell.Body);
            CreateCodeCard(shell.Body, DemoCode.SignIn);
            UiBuilder.SetField(shell.Controller, "badge", SampleComponent(badge, "PlayerBadge"));
            UiBuilder.SetField(shell.Controller, "accountPanel", panel);
        }

        private static void BuildLeaderboard(Prefabs prefabs)
        {
            var shell = CreateShell("Leaderboard", "Submit a score, see the top players");
            var panel = BuildLeaderboardSection(shell.Body, prefabs);
            CreateCodeCard(shell.Body, DemoCode.Leaderboard);
            UiBuilder.SetField(shell.Controller, "leaderboardPanel", panel);
        }

        private static void BuildCloudSave()
        {
            var shell = CreateShell("Cloud Save", "Save and load player progress", scroll: true);
            var panel = BuildCloudSaveSection(shell.Body);
            CreateCodeCard(shell.Body, DemoCode.CloudSave);
            UiBuilder.SetField(shell.Controller, "cloudSavePanel", panel);
        }

        private static void BuildRewards(Prefabs prefabs)
        {
            var shell = CreateShell("Rewards", "Daily reward and coin wallet", scroll: true);
            var badge = InstantiatePrefab(prefabs.PlayerBadge, shell.Body);
            var panel = BuildRewardsSection(shell.Body, prefabs);
            CreateCodeCard(shell.Body, DemoCode.Rewards);
            UiBuilder.SetField(shell.Controller, "badge", SampleComponent(badge, "PlayerBadge"));
            UiBuilder.SetField(shell.Controller, "rewardsPanel", panel);
        }

        private static void BuildRemoteConfig(Prefabs prefabs)
        {
            var shell = CreateShell("Remote Config", "Live values from your dashboard");
            var panel = BuildConfigSection(shell.Body, prefabs);
            CreateCodeCard(shell.Body, DemoCode.RemoteConfig);
            UiBuilder.SetField(shell.Controller, "configPanel", panel);
        }

        private static void BuildTestData()
        {
            var shell = CreateShell("Test Data", "Add data and check every service");
            var (scroll, content) = UiBuilder.CreateScrollList("Scroll", shell.Body, 20);
            UiBuilder.SetSize(scroll.gameObject, -1, -1, -1, 1);
            content.GetComponent<VerticalLayoutGroup>().padding = new RectOffset(0, 0, 0, 24);

            var quick = CreateCard("QuickTest", content, "Quick test");
            CreateNote(quick.transform, "Runs every service once and writes the result to the log.");
            var runAll = CreateStyledButton("RunAll", quick.transform, "Run all tests", Style.Primary);
            UiBuilder.SetSize(runAll.gameObject, 96);

            var leaderboard = CreateCard("Leaderboard", content, "Leaderboard");
            var lbInputs = CreateFields(leaderboard.transform,
                new Field("Slug", "Slug", "coins", "coins", 300),
                new Field("Score", "Score", string.Empty, "e.g. 120", -1, TMP_InputField.ContentType.DecimalNumber));
            var lbButtons = CreateButtonRow(leaderboard.transform, ("Submit", "Submit", Style.Primary), ("Random", "Random score", Style.Secondary), ("TestPlayers", "+5 players", Style.Secondary));
            CreateNote(leaderboard.transform, "+5 players adds test guests with random scores. They count toward your plan's player limit.");

            var cloud = CreateCard("CloudSave", content, "Cloud save");
            var cloudInputs = CreateFields(cloud.transform,
                new Field("Key", "Key", "test_data", "test_data", 300),
                new Field("Json", "JSON", "{\"level\":3,\"coins\":120}", "{\"level\":3}"));
            var cloudButtons = CreateButtonRow(cloud.transform, ("Save", "Save", Style.Primary), ("Load", "Load", Style.Secondary), ("List", "List", Style.Secondary), ("Delete", "Delete", Style.Danger));

            var rewards = CreateCard("Rewards", content, "Rewards");
            var rewardInputs = CreateFields(rewards.transform, new Field("RewardKey", "Reward key", "daily_coins", "daily_coins"));
            var rewardButtons = CreateButtonRow(rewards.transform, ("Claim", "Claim", Style.Primary), ("Wallet", "Wallet", Style.Secondary), ("List", "List", Style.Secondary));

            var player = CreateCard("Player", content, "Player");
            var playerInputs = CreateFields(player.transform, new Field("Name", "Display name", string.Empty, "e.g. Maja"));
            var playerButtons = CreateButtonRow(player.transform, ("SetName", "Set name", Style.Primary), ("NewGuest", "New guest", Style.Secondary));
            CreateNote(player.transform, "New guest signs out and starts a fresh player, so you can add scores as someone else.");

            var config = CreateCard("RemoteConfig", content, "Remote config");
            var configInputs = CreateFields(config.transform, new Field("ConfigKey", "Key", "welcome_message", "welcome_message"));
            var configButtons = CreateButtonRow(config.transform, ("Get", "Get value", Style.Primary), ("Fetch", "Fetch all", Style.Secondary));

            var log = UiBuilder.CreatePanel("Log", shell.Body, UiTheme.Panel).gameObject;
            UiBuilder.AddVertical(log, 8, 24);
            UiBuilder.SetSize(log, 300);
            var logHeader = UiBuilder.CreateRect("Header", log.transform);
            UiBuilder.AddHorizontal(logHeader, 12, 0);
            UiBuilder.SetSize(logHeader, 48);
            var logTitle = UiBuilder.CreateText("Title", logHeader.transform, "Log", UiTheme.Small, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(logTitle.gameObject, -1, -1, 1);
            var clear = UiBuilder.CreateButton("Clear", logHeader.transform, "Clear", UiTheme.Panel2, UiTheme.Text, UiTheme.Tiny, UiTheme.Pill(48));
            UiBuilder.SetSize(clear.gameObject, -1, 130);
            var logText = UiBuilder.CreateText("Text", log.transform, string.Empty, UiTheme.Tiny, UiTheme.Text, TextAlignmentOptions.TopLeft);
            logText.overflowMode = TextOverflowModes.Truncate;
            UiBuilder.SetSize(logText.gameObject, -1, -1, -1, 1);

            var panel = UiBuilder.AddSampleComponent(shell.Body.gameObject, "TestDataPanel");
            UiBuilder.SetField(panel, "runAllButton", runAll);
            UiBuilder.SetField(panel, "slugInput", lbInputs[0]);
            UiBuilder.SetField(panel, "scoreInput", lbInputs[1]);
            UiBuilder.SetField(panel, "submitButton", lbButtons[0]);
            UiBuilder.SetField(panel, "randomScoreButton", lbButtons[1]);
            UiBuilder.SetField(panel, "testPlayersButton", lbButtons[2]);
            UiBuilder.SetField(panel, "saveKeyInput", cloudInputs[0]);
            UiBuilder.SetField(panel, "saveJsonInput", cloudInputs[1]);
            UiBuilder.SetField(panel, "saveButton", cloudButtons[0]);
            UiBuilder.SetField(panel, "loadButton", cloudButtons[1]);
            UiBuilder.SetField(panel, "listSavesButton", cloudButtons[2]);
            UiBuilder.SetField(panel, "deleteSaveButton", cloudButtons[3]);
            UiBuilder.SetField(panel, "rewardKeyInput", rewardInputs[0]);
            UiBuilder.SetField(panel, "claimButton", rewardButtons[0]);
            UiBuilder.SetField(panel, "walletButton", rewardButtons[1]);
            UiBuilder.SetField(panel, "listRewardsButton", rewardButtons[2]);
            UiBuilder.SetField(panel, "nameInput", playerInputs[0]);
            UiBuilder.SetField(panel, "setNameButton", playerButtons[0]);
            UiBuilder.SetField(panel, "newGuestButton", playerButtons[1]);
            UiBuilder.SetField(panel, "configKeyInput", configInputs[0]);
            UiBuilder.SetField(panel, "getConfigButton", configButtons[0]);
            UiBuilder.SetField(panel, "fetchConfigButton", configButtons[1]);
            UiBuilder.SetField(panel, "logLabel", logText);
            UiBuilder.SetField(panel, "clearLogButton", clear);
        }

        private static void BuildDocs()
        {
            var shell = CreateShell("Docs", "Short code examples to copy", signIn: false, scroll: true);
            var intro = UiBuilder.CreateText("Intro", shell.Body, "Add <b>using MajacloudSdk;</b> to your script. Everything starts from the static <b>Majacloud</b> class.", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.TopLeft);
            intro.overflowMode = TextOverflowModes.Overflow;
            foreach (var section in DemoCode.Docs)
            {
                var card = CreateCard(section.Title, shell.Body, section.Title);
                var text = UiBuilder.CreateText("Text", card.transform, section.Text, UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.TopLeft);
                text.overflowMode = TextOverflowModes.Overflow;
                CreateCodeCard(card.transform, section.Code);
            }
            var footer = UiBuilder.CreateText("Footer", shell.Body, "More: the README in Assets/Majacloud and the docs on your Majacloud site.", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.Center);
            UiBuilder.SetSize(footer.gameObject, 80);
        }

        // --- Service sections (shared by the All-in-One scene and the single scenes) ---------

        private static Component BuildLeaderboardSection(Transform page, Prefabs prefabs)
        {
            var view = InstantiatePrefab(prefabs.LeaderboardView, page);
            // The prefab is 900 tall on its own; inside a page it fills the space but may shrink for short screens.
            UiBuilder.SetSize(view, -1, -1, -1, 1).minHeight = 360;
            var submitRow = UiBuilder.CreateRect("SubmitRow", page);
            UiBuilder.AddHorizontal(submitRow, 12, 0);
            UiBuilder.SetSize(submitRow, 96);
            var scoreInput = UiBuilder.CreateInput("ScoreInput", submitRow.transform, "Score, e.g. 120", TMP_InputField.ContentType.DecimalNumber);
            UiBuilder.SetSize(scoreInput.gameObject, -1, -1, 1);
            var submit = CreateStyledButton("Submit", submitRow.transform, "Submit score", Style.Primary);
            UiBuilder.SetSize(submit.gameObject, -1, 320);
            var panel = UiBuilder.AddSampleComponent(page.gameObject, "LeaderboardPanel");
            UiBuilder.SetField(panel, "view", SampleComponent(view, "LeaderboardView"));
            UiBuilder.SetField(panel, "scoreInput", scoreInput);
            UiBuilder.SetField(panel, "submitButton", submit);
            return panel;
        }

        private static Component BuildCloudSaveSection(Transform page)
        {
            var card = CreateCard("CloudSaveCard", page, "Player progress");
            var numbers = CreateFields(card.transform,
                new Field("Level", "Level", string.Empty, "e.g. 3", -1, TMP_InputField.ContentType.IntegerNumber),
                new Field("Coins", "Coins", string.Empty, "e.g. 120", -1, TMP_InputField.ContentType.IntegerNumber));
            var nickname = CreateFields(card.transform, new Field("Nickname", "Nickname", string.Empty, "e.g. Maja"));
            var buttons = CreateButtonRow(card.transform, ("Save", "Save", Style.Primary), ("Load", "Load", Style.Secondary), ("Delete", "Delete", Style.Danger));
            var revisionLabel = UiBuilder.CreateText("Revision", card.transform, "Revision: –", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(revisionLabel.gameObject, 40);
            var status = UiBuilder.CreateText("Status", card.transform, string.Empty, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(status.gameObject, 48);
            var panel = UiBuilder.AddSampleComponent(page.gameObject, "CloudSavePanel");
            UiBuilder.SetField(panel, "levelInput", numbers[0]);
            UiBuilder.SetField(panel, "coinsInput", numbers[1]);
            UiBuilder.SetField(panel, "nicknameInput", nickname[0]);
            UiBuilder.SetField(panel, "saveButton", buttons[0]);
            UiBuilder.SetField(panel, "loadButton", buttons[1]);
            UiBuilder.SetField(panel, "deleteButton", buttons[2]);
            UiBuilder.SetField(panel, "revisionLabel", revisionLabel);
            UiBuilder.SetField(panel, "statusLabel", status);
            return panel;
        }

        private static Component BuildRewardsSection(Transform page, Prefabs prefabs)
        {
            var card = CreateCard("RewardsCard", page, "Daily reward");
            var rewardButton = InstantiatePrefab(prefabs.DailyRewardButton, card.transform);
            var walletLabel = UiBuilder.CreateText("Wallet", card.transform, "Coins: –", UiTheme.Heading, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(walletLabel.gameObject, 64);
            var walletRefresh = CreateStyledButton("RefreshWallet", card.transform, "Refresh wallet", Style.Secondary);
            UiBuilder.SetSize(walletRefresh.gameObject, 88);
            var status = UiBuilder.CreateText("Status", card.transform, "The server checks the cooldown, so the reward can't be claimed twice.", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.TopLeft);
            UiBuilder.SetSize(status.gameObject, 80);
            var panel = UiBuilder.AddSampleComponent(page.gameObject, "RewardsPanel");
            UiBuilder.SetField(panel, "rewardButton", SampleComponent(rewardButton, "DailyRewardButton"));
            UiBuilder.SetField(panel, "walletLabel", walletLabel);
            UiBuilder.SetField(panel, "refreshButton", walletRefresh);
            UiBuilder.SetField(panel, "statusLabel", status);
            return panel;
        }

        private static Component BuildConfigSection(Transform page, Prefabs prefabs)
        {
            var card = CreateCard("ConfigCard", page, "Live values");
            UiBuilder.SetSize(card, -1, -1, -1, 1);
            var header = UiBuilder.CreateRect("Header", card.transform);
            UiBuilder.AddHorizontal(header, 12, 0);
            UiBuilder.SetSize(header, 64);
            var info = UiBuilder.CreateText("Info", header.transform, "Not loaded", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(info.gameObject, -1, -1, 1);
            var refresh = UiBuilder.CreateButton("Refresh", header.transform, "Refresh", UiTheme.Panel2, UiTheme.Text, UiTheme.Small);
            UiBuilder.SetSize(refresh.gameObject, 64, 170);
            var (scroll, content) = UiBuilder.CreateScrollList("List", card.transform, 8);
            UiBuilder.SetSize(scroll.gameObject, -1, -1, -1, 1);
            var panel = UiBuilder.AddSampleComponent(page.gameObject, "ConfigPanel");
            UiBuilder.SetField(panel, "headerLabel", info);
            UiBuilder.SetField(panel, "refreshButton", refresh);
            UiBuilder.SetField(panel, "content", content);
            UiBuilder.SetField(panel, "rowPrefab", SampleComponent(prefabs.ConfigRow, "ConfigRowView"));
            return panel;
        }

        private static Component BuildAccountSection(Transform page)
        {
            var card = CreateCard("AccountCard", page, "Account");
            UiBuilder.SetSize(card, -1, -1, -1, 1);
            var playerIdLabel = UiBuilder.CreateText("PlayerId", card.transform, "Player ID: –", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(playerIdLabel.gameObject, 44);
            var authTypeLabel = UiBuilder.CreateText("AuthType", card.transform, "Sign-in: –", UiTheme.Small, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(authTypeLabel.gameObject, 44);
            var nameRow = UiBuilder.CreateRect("NameRow", card.transform);
            UiBuilder.AddHorizontal(nameRow, 12, 0);
            UiBuilder.SetSize(nameRow, 92);
            var nameInput = UiBuilder.CreateInput("NameInput", nameRow.transform, "Display name");
            UiBuilder.SetSize(nameInput.gameObject, -1, -1, 1);
            var saveName = CreateStyledButton("SaveName", nameRow.transform, "Save", Style.Secondary);
            UiBuilder.SetSize(saveName.gameObject, -1, 200);
            var linkButton = CreateStyledButton("Link", card.transform, "Link account (opens browser)", Style.Primary);
            UiBuilder.SetSize(linkButton.gameObject, 92);
            var signOut = CreateStyledButton("SignOut", card.transform, "Sign out", Style.Danger);
            UiBuilder.SetSize(signOut.gameObject, 92);
            var logLabel = UiBuilder.CreateText("Log", card.transform, string.Empty, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.TopLeft);
            UiBuilder.SetSize(logLabel.gameObject, 160, -1, -1, 1);
            logLabel.overflowMode = TextOverflowModes.Truncate;
            var panel = UiBuilder.AddSampleComponent(page.gameObject, "AccountPanel");
            UiBuilder.SetField(panel, "playerIdLabel", playerIdLabel);
            UiBuilder.SetField(panel, "authTypeLabel", authTypeLabel);
            UiBuilder.SetField(panel, "nameInput", nameInput);
            UiBuilder.SetField(panel, "saveNameButton", saveName);
            UiBuilder.SetField(panel, "linkButton", linkButton);
            UiBuilder.SetField(panel, "signOutButton", signOut);
            UiBuilder.SetField(panel, "logLabel", logLabel);
            return panel;
        }

        // --- Building blocks ------------------------------------------------------------------

        /// <summary>Camera-less scene skeleton: event system, controller, canvas, centred column, header and toast.</summary>
        private static Shell CreateShell(string title, string subtitle, bool signIn = true, bool scroll = false)
        {
            UiBuilder.CreateEventSystem();
            var starter = new GameObject("Starter");
            var controller = UiBuilder.AddSampleComponent(starter, "StarterController");
            UiBuilder.SetField(controller, "signInOnStart", signIn);

            var canvas = UiBuilder.CreateCanvas("Canvas", Reference, 0.5f);
            var background = UiBuilder.CreatePanel("Background", canvas.transform, UiTheme.Bg, 0f);
            UiBuilder.Stretch(background.rectTransform, 0, 0);

            // A column capped at a phone-like width, so the same scene works in portrait and landscape.
            var column = UiBuilder.CreateRect("Column", canvas.transform);
            var columnRect = (RectTransform)column.transform;
            columnRect.anchorMin = new Vector2(0.5f, 0);
            columnRect.anchorMax = new Vector2(0.5f, 1);
            columnRect.pivot = new Vector2(0.5f, 0.5f);
            columnRect.anchoredPosition = Vector2.zero;
            columnRect.sizeDelta = new Vector2(1000, 0);
            UiBuilder.AddSampleComponent(column, "MaxWidthColumn");
            var layout = UiBuilder.AddVertical(column, 24, 0);
            layout.padding = new RectOffset(0, 0, 36, 36);

            if (title != null) CreateHeader(column.transform, title, subtitle);

            Transform body;
            if (scroll)
            {
                var (scrollRect, content) = UiBuilder.CreateScrollList("Body", column.transform, 20);
                UiBuilder.SetSize(scrollRect.gameObject, -1, -1, -1, 1);
                content.GetComponent<VerticalLayoutGroup>().padding = new RectOffset(0, 0, 0, 120);
                body = content;
            }
            else
            {
                var page = UiBuilder.CreateRect("Body", column.transform);
                UiBuilder.AddVertical(page, 20, 0);
                UiBuilder.SetSize(page, -1, -1, -1, 1);
                body = page.transform;
            }

            UiBuilder.SetField(controller, "toast", CreateToast(canvas.transform));
            return new Shell { Controller = controller, Body = body };
        }

        private static void CreateHeader(Transform parent, string title, string subtitle)
        {
            var header = UiBuilder.CreateRect("Header", parent);
            UiBuilder.AddHorizontal(header, 20, 0);
            UiBuilder.SetSize(header, 100);
            var back = UiBuilder.CreateButton("Back", header.transform, "‹", UiTheme.Panel, UiTheme.Text, 64, UiTheme.Pill(100));
            back.GetComponentInChildren<TextMeshProUGUI>().rectTransform.offsetMin = new Vector2(16, 14);
            UiBuilder.SetSize(back.gameObject, -1, 100);
            AddSceneLink(back.gameObject, MajacloudEditorPaths.HomeScenePath);
            var titles = UiBuilder.CreateRect("Titles", header.transform);
            UiBuilder.AddVertical(titles, 0, 0, TextAnchor.MiddleLeft);
            UiBuilder.SetSize(titles, -1, -1, 1);
            var titleText = UiBuilder.CreateText("Title", titles.transform, title, UiTheme.Heading, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(titleText.gameObject, 52);
            var subtitleText = UiBuilder.CreateText("Subtitle", titles.transform, subtitle, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft);
            UiBuilder.SetSize(subtitleText.gameObject, 34);
            var logo = UiBuilder.CreateImage("Logo", header.transform, SoftArt.Logo);
            UiBuilder.SetSize(logo.gameObject, -1, 72);
        }

        private static Component CreateToast(Transform canvas)
        {
            var toastGo = UiBuilder.CreatePanel("Toast", canvas, UiTheme.Dark, UiTheme.Pill(96)).gameObject;
            var toastRect = (RectTransform)toastGo.transform;
            toastRect.anchorMin = new Vector2(0.5f, 0);
            toastRect.anchorMax = new Vector2(0.5f, 0);
            toastRect.pivot = new Vector2(0.5f, 0);
            toastRect.anchoredPosition = new Vector2(0, 60);
            toastRect.sizeDelta = new Vector2(900, 96);
            var toastGroup = toastGo.AddComponent<CanvasGroup>();
            toastGroup.alpha = 0;
            toastGroup.blocksRaycasts = false;
            var toastText = UiBuilder.CreateText("Text", toastGo.transform, string.Empty, UiTheme.Small, Color.white, TextAlignmentOptions.Center);
            UiBuilder.Stretch(toastText.rectTransform, 32, 12);
            var toast = UiBuilder.AddSampleComponent(toastGo, "StatusToast");
            UiBuilder.SetField(toast, "group", toastGroup);
            UiBuilder.SetField(toast, "label", toastText);
            UiBuilder.SetField(toast, "background", toastGo.GetComponent<Image>());
            return toast;
        }

        private static GameObject CreatePage(string name, Transform parent)
        {
            var page = UiBuilder.CreateRect(name, parent);
            UiBuilder.Stretch((RectTransform)page.transform, 0, 0);
            UiBuilder.AddVertical(page, 16, 0, TextAnchor.UpperCenter, false);
            return page;
        }

        private static GameObject CreateCard(string name, Transform parent, string title)
        {
            var card = UiBuilder.CreatePanel(name, parent, UiTheme.Panel).gameObject;
            UiBuilder.AddVertical(card, 14, 28);
            var heading = UiBuilder.CreateText("Heading", card.transform, title, UiTheme.Body, UiTheme.Text, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(heading.gameObject, 52);
            return card;
        }

        /// <summary>A short text that sizes itself to its content.</summary>
        private static void CreateNote(Transform parent, string text)
        {
            var note = UiBuilder.CreateText("Note", parent, text, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.TopLeft);
            note.overflowMode = TextOverflowModes.Overflow;
        }

        /// <summary>Monospaced code block with a Copy button.</summary>
        private static void CreateCodeCard(Transform parent, string code)
        {
            var card = UiBuilder.CreatePanel("Code", parent, UiTheme.Code, UiTheme.RadiusControl).gameObject;
            UiBuilder.AddVertical(card, 8, 24);
            var header = UiBuilder.CreateRect("Header", card.transform);
            UiBuilder.AddHorizontal(header, 12, 0);
            UiBuilder.SetSize(header, 44);
            var label = UiBuilder.CreateText("Label", header.transform, "C# EXAMPLE", UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
            UiBuilder.SetSize(label.gameObject, -1, -1, 1);
            var copy = UiBuilder.CreateButton("Copy", header.transform, "Copy", UiTheme.Panel, UiTheme.AccentInk, UiTheme.Tiny, UiTheme.Pill(44));
            UiBuilder.SetSize(copy.gameObject, -1, 130);
            var copyText = UiBuilder.AddSampleComponent(copy.gameObject, "CopyText");
            UiBuilder.SetField(copyText, "text", code);
            var text = UiBuilder.CreateText("Text", card.transform, "<noparse>" + code + "</noparse>", UiTheme.Tiny, UiTheme.CodeText, TextAlignmentOptions.TopLeft);
            text.overflowMode = TextOverflowModes.Overflow;
            text.lineSpacing = 8;
        }

        private static Button CreateStyledButton(string name, Transform parent, string label, Style style)
        {
            switch (style)
            {
                case Style.Primary: return UiBuilder.CreateButton(name, parent, label, UiTheme.Accent, UiTheme.OnAccent, UiTheme.Small);
                case Style.Danger: return UiBuilder.CreateButton(name, parent, label, UiTheme.DangerSoft, UiTheme.Danger, UiTheme.Small);
                default: return UiBuilder.CreateButton(name, parent, label, UiTheme.Panel2, UiTheme.Text, UiTheme.Small);
            }
        }

        private static Button[] CreateButtonRow(Transform parent, params (string name, string label, Style style)[] buttons)
        {
            var row = UiBuilder.CreateRect("Buttons", parent);
            UiBuilder.AddHorizontal(row, 12, 0);
            UiBuilder.SetSize(row, 88);
            var result = new Button[buttons.Length];
            for (int i = 0; i < buttons.Length; i++)
            {
                result[i] = CreateStyledButton(buttons[i].name, row.transform, buttons[i].label, buttons[i].style);
                UiBuilder.SetSize(result[i].gameObject, -1, -1, 1);
            }
            return result;
        }

        /// <summary>A row of captions over a row of inputs. Width -1 means "share the rest".</summary>
        private static TMP_InputField[] CreateFields(Transform parent, params Field[] fields)
        {
            var captions = UiBuilder.CreateRect("Captions", parent);
            UiBuilder.AddHorizontal(captions, 12, 0);
            UiBuilder.SetSize(captions, 30);
            var inputs = UiBuilder.CreateRect("Inputs", parent);
            UiBuilder.AddHorizontal(inputs, 12, 0);
            UiBuilder.SetSize(inputs, 88);
            var result = new TMP_InputField[fields.Length];
            for (int i = 0; i < fields.Length; i++)
            {
                var field = fields[i];
                float flexible = field.Width < 0 ? 1 : -1;
                var caption = UiBuilder.CreateText(field.Name + "Caption", captions.transform, field.Caption, UiTheme.Tiny, UiTheme.Muted, TextAlignmentOptions.MidlineLeft, FontStyles.Bold);
                UiBuilder.SetSize(caption.gameObject, -1, Mathf.Max(field.Width, 0), flexible);
                result[i] = UiBuilder.CreateInput(field.Name + "Input", inputs.transform, field.Placeholder, field.Type, field.Value);
                UiBuilder.SetSize(result[i].gameObject, -1, Mathf.Max(field.Width, 0), flexible);
            }
            return result;
        }

        private static void AddSceneLink(GameObject button, string scenePath)
        {
            var link = UiBuilder.AddSampleComponent(button, "SceneLink");
            UiBuilder.SetField(link, "scenePath", scenePath);
        }

        private static GameObject InstantiatePrefab(GameObject prefab, Transform parent)
        {
            var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
            instance.transform.SetParent(parent, false);
            return instance;
        }

        private static Component SampleComponent(GameObject go, string typeName)
        {
            var type = Type.GetType("MajacloudSdk.Samples.Starter." + typeName + ", Majacloud.Samples.Starter");
            return type != null ? go.GetComponent(type) : null;
        }
    }
}
