using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace Unity.VisualScripting
{
    // Important, see static constructor below for more details
    [InitializeOnLoad]
    public static class VSUsageUtility
    {
        /* Need to be careful with this constructor:
         * A major difference between Visual Scripting and other packages is that VS is installed by default in all projects
         * This makes it discoverable, but also means that potentially all users pay the cost of our background processes
         *
         * This utility let's us isolate users who *actually* are using Visual Scripting from users who aren't.
         * For those who aren't, we want to minimize the cost they pay. InitializeOnLoad triggers on every domain reload
         * so we need to be careful with what we do in this constructor and cache what we can to avoid calculation.
         */
        static VSUsageUtility()
        {
            CheckAndSetIsVisualScriptingUsed();
            DoInitializeOnLoadCalls();
        }

        private static void CheckAndSetIsVisualScriptingUsed()
        {
            // Using the SessionState API, this lets us cache our boolean persisted through domain reloads, but cleared
            // on Unity exit
            var sessionStateSet = SessionState.GetBool("Unity.VisualScripting.IsVisualScriptingUsedSet", false);
            if (sessionStateSet)
            {
                isVisualScriptingUsed = SessionState.GetBool("Unity.VisualScripting.IsVisualScriptingUsed", false);
                return;
            }

            // Everything under here is only calculated once per Unity session start (project open)
            var isVSUsed = false;

            // If we want to check on any state once per editor start-up that tells us if VS is being used, do that here

            // in batchmode or when someone builds a project using vs without opening a graph themselves, we still need
            // to consider it used to ensure the AOTPrebuilder/link.xml generation still kicks in
            isVSUsed = AssetDatabase.IsValidFolder(PluginPaths.ASSETS_FOLDER_BOLT_GENERATED);

            isVSUsed = isVSUsed || IsOlderVersionOfVSUsed();

            isVisualScriptingUsed = isVSUsed;
        }

        // Note: This is currently only possible because all our InitializeOnLoad calls were in Core. If we ever
        // need to put something in Flow or State, we might need to move this entire class to a shared assembly that
        // references those assemblies.
        private static void DoInitializeOnLoadCalls()
        {
            if (!isVisualScriptingUsed)
                return;

            if (_initializeOnLoadExecuted)
                return;

            _initializeOnLoadExecuted = true;

            EditorDebugUtility.DeleteDebugLogFile();

            EditorPlatformUtility.InitializeActiveBuildTarget();

            EditorApplicationUtility.Initialize();

            EditorTimeUtility.Initialize();

            UnityAPI.Initialize();

            PackageEventListener.SubscribeToEvents();

            PluginContainer.InitializeOnLoad();

            XmlDocumentation.Initialize();
        }

        private static bool IsOlderVersionOfVSUsed()
        {
            // Instantiating our deprecated loaders explicitly to avoid plugin container / plugin initialization logic
            // Is ordered in descending order (newest versions first)
            var deprecatedLoaders = new PluginDeprecatedSavedVersionLoader[]
            {
                new DeprecatedSavedVersionLoader_1_6_1(null),
                new DeprecatedSavedVersionLoader_1_5_1(null),
                new DeprecatedSavedVersionLoader_Bolt_AssetStore(null)
            };

            // We don't need to use the old loaders for every plugin, just core, because we just want to know *if*
            // an older version is being used or not
            return deprecatedLoaders.Select(savedVersionLoader => savedVersionLoader.Run(out var loadedVersion))
                                    .Any(success => success);
        }

        public static bool isVisualScriptingUsed
        {
            get => _isVisualScriptingUsed;
            set
            {
                SessionState.SetBool("Unity.VisualScripting.IsVisualScriptingUsedSet", true);
                SessionState.SetBool("Unity.VisualScripting.IsVisualScriptingUsed", value);

                if (_isVisualScriptingUsed == value)
                    return;

                _isVisualScriptingUsed = value;

                // The first time we determine that Visual Scripting is being used, we trigger other things
                if (_isVisualScriptingUsed)
                {
                    DoInitializeOnLoadCalls();

                    // Ensure our savedVersion is serialized for future migrations
                    BoltCore.Manifest.savedVersion = BoltCore.Manifest.savedVersion;

                    // Ensure that our nodes / units are imported and cached
                    PluginContainer.ImportUnits();
                }
            }
        }
        private static bool _isVisualScriptingUsed = false;

        // Used to protect against doing the initialize on load calls twice in the same app domain frame.
        // This can only happen on editor start, if we set _isVisualScriptingUsed to true
        private static bool _initializeOnLoadExecuted = false;
    }
}