// Copyright (c) 2026 EzAdmin. All rights reserved.
// Licensed under the EzAdmin Client License. See LICENSE-CLIENT.txt.

using System.Collections.Generic;
using System.IO;
using EzAdmin.Client;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

namespace EzAdmin.EditorTools
{
    public sealed class LoadingSceneOptions
    {
        public bool VR = true;
        public bool SignIn = true;
        public string GameName = "";
        public Sprite Logo;
        public Sprite Background;
        public Color Accent = new Color32(0x22, 0xC5, 0x5E, 0xFF);
        public bool ReturnToGame = true;
        public string NextScene = "";
    }

    public static class EzAdminLoadingSceneMaker
    {
        public const string Folder = "Assets/EzAdmin Scenes";
        public const string ScenePath = Folder + "/EzAdmin Loading.unity";

        [MenuItem("Tools/EzAdmin/Create Loading Screen Scene", false, 20)]
        public static void CreateFromMenu()
        {
            string result = Create(new LoadingSceneOptions { GameName = PlayerSettings.productName }, true);
            if (!string.IsNullOrEmpty(result)) EditorUtility.DisplayDialog("EzAdmin", result, "OK");
        }

        public static bool Exists => File.Exists(ScenePath);

        public static string Create(LoadingSceneOptions options, bool ask)
        {
            if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return "";

            var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
            string firstGame = "";
            foreach (var s in scenes)
            {
                if (s.enabled && s.path != ScenePath)
                {
                    firstGame = s.path;
                    break;
                }
            }
            if (!string.IsNullOrEmpty(options.NextScene) && File.Exists(options.NextScene)) firstGame = options.NextScene;
            var open = SceneManager.GetActiveScene();
            if (string.IsNullOrEmpty(firstGame) && !string.IsNullOrEmpty(open.path) && open.path != ScenePath)
                firstGame = open.path;
            if (string.IsNullOrEmpty(firstGame))
                return "Save your game scene first, or add it to File > Build Settings, so the loading screen knows " +
                       "what to open after it.";
            string comeBack = !string.IsNullOrEmpty(open.path) && open.path != ScenePath ? open.path : firstGame;

            if (ask && Exists && !EditorUtility.DisplayDialog("EzAdmin",
                    "There is already an EzAdmin loading scene. Make a fresh one?", "Make a fresh one", "Cancel"))
                return "";

            if (!AssetDatabase.IsValidFolder(Folder)) AssetDatabase.CreateFolder("Assets", "EzAdmin Scenes");

            var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);

            var cameraObject = new GameObject("Main Camera", typeof(Camera), typeof(AudioListener));
            cameraObject.tag = "MainCamera";
            cameraObject.transform.position = new Vector3(0f, 1.6f, 0f);
            var camera = cameraObject.GetComponent<Camera>();
            camera.clearFlags = CameraClearFlags.SolidColor;
            camera.backgroundColor = new Color32(0x0B, 0x0C, 0x10, 0xFF);
            camera.nearClipPlane = 0.05f;
            if (options.VR) TryAddTrackedPose(cameraObject);

            var ez = new GameObject("EzAdmin", typeof(EzAdminClient));
            if (options.SignIn) ez.AddComponent<EzAdminAuth>();

            var loader = new GameObject("EzAdmin Loading Screen", typeof(EzAdminLoadingScreen));
            var so = new SerializedObject(loader.GetComponent<EzAdminLoadingScreen>());
            so.FindProperty("_nextScene").stringValue = Path.GetFileNameWithoutExtension(firstGame);
            so.FindProperty("_gameName").stringValue = string.IsNullOrEmpty(options.GameName)
                ? PlayerSettings.productName : options.GameName;
            so.FindProperty("_gameLogo").objectReferenceValue = options.Logo;
            so.FindProperty("_backgroundImage").objectReferenceValue = options.Background;
            so.FindProperty("_accent").colorValue = options.Accent;
            so.FindProperty("_mode").enumValueIndex = options.VR ? 0 : 2;
            so.ApplyModifiedPropertiesWithoutUndo();
            BuildDesign(loader.GetComponent<EzAdminLoadingScreen>());

            EditorSceneManager.SaveScene(scene, ScenePath);

            scenes.RemoveAll(s => s.path == ScenePath);
            if (!scenes.Exists(s => s.path == firstGame)) scenes.Add(new EditorBuildSettingsScene(firstGame, true));
            scenes.Insert(0, new EditorBuildSettingsScene(ScenePath, true));
            EditorBuildSettings.scenes = scenes.ToArray();

            if (options.ReturnToGame && !string.IsNullOrEmpty(comeBack)) EditorSceneManager.OpenScene(comeBack);
            else Selection.activeGameObject = loader;

            return "Made " + ScenePath + " and put it first in Build Settings. It signs the player in, loads their items, " +
                   "then opens " + Path.GetFileNameWithoutExtension(firstGame) + ".\n\nThe screen is built into that scene as " +
                   "normal objects under 'Loading Screen', so you can move or restyle any of it. The name, logo, colours " +
                   "and words are set on the EzAdmin Loading Screen object.";
        }

        public const string ArtFolder = Folder + "/Loading Screen Art";

        public static void BuildDesign(EzAdminLoadingScreen screen)
        {
            if (screen == null) return;
            if (!AssetDatabase.IsValidFolder(Folder)) AssetDatabase.CreateFolder("Assets", "EzAdmin Scenes");
            if (!AssetDatabase.IsValidFolder(ArtFolder)) AssetDatabase.CreateFolder(Folder, "Loading Screen Art");
            EzAdminLoadingScreen.SpriteStore = SaveSprite;
            try
            {
                Undo.RegisterFullObjectHierarchyUndo(screen.gameObject, "Build loading screen");
                screen.BuildDesign();
                EditorUtility.SetDirty(screen);
                EditorSceneManager.MarkSceneDirty(screen.gameObject.scene);
            }
            finally
            {
                EzAdminLoadingScreen.SpriteStore = null;
            }
        }

        private static Sprite SaveSprite(string key, Sprite made)
        {
            string path = ArtFolder + "/" + key + ".png";
            var existing = AssetDatabase.LoadAssetAtPath<Sprite>(path);
            if (existing != null) return existing;
            byte[] png;
            try
            {
                png = made.texture.EncodeToPNG();
            }
            catch (System.Exception)
            {
                return made;
            }
            File.WriteAllBytes(path, png);
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport);
            var importer = AssetImporter.GetAtPath(path) as TextureImporter;
            if (importer != null)
            {
                importer.textureType = TextureImporterType.Sprite;
                importer.spriteImportMode = SpriteImportMode.Single;
                importer.spritePixelsPerUnit = 100f;
                importer.spriteBorder = made.border;
                importer.mipmapEnabled = true;
                importer.alphaIsTransparency = true;
                importer.wrapMode = TextureWrapMode.Clamp;
                importer.filterMode = FilterMode.Trilinear;
                importer.textureCompression = TextureImporterCompression.Uncompressed;
                importer.SaveAndReimport();
            }
            var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
            return sprite != null ? sprite : made;
        }

        private static void TryAddTrackedPose(GameObject cameraObject)
        {
            var type = System.Type.GetType("UnityEngine.SpatialTracking.TrackedPoseDriver, UnityEngine.SpatialTracking")
                       ?? System.Type.GetType("UnityEngine.InputSystem.XR.TrackedPoseDriver, Unity.InputSystem");
            if (type != null) cameraObject.AddComponent(type);
        }

        [MenuItem("Tools/EzAdmin/Remove Loading Screen From Build", false, 21)]
        public static void Remove()
        {
            var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
            int removed = scenes.RemoveAll(s => s.path == ScenePath);
            EditorBuildSettings.scenes = scenes.ToArray();
            EditorUtility.DisplayDialog("EzAdmin", removed > 0
                ? "The loading screen is out of Build Settings. Your game starts on its own first scene again."
                : "The loading screen was not in Build Settings.", "OK");
        }
    }
}
