/*************************************************************************************************
 * Copyright 2022-2025 Theai, Inc. dba Inworld AI
 *
 * Use of this source code is governed by the Inworld.ai Software Development Kit License Agreement
 * that can be found in the LICENSE.md file or at https://www.inworld.ai/sdk-license
 *************************************************************************************************/
#if UNITY_EDITOR
using System;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;

namespace Inworld
{
    [InitializeOnLoad]
    internal static class EssentialManifestUpdateChecker
    {
        const string k_CheckedThisSessionKey = "Inworld.EssentialManifestUpdateChecker.Checked";
        const string k_DoNotShowAgainKey = "Inworld.EssentialManifestUpdateChecker.DoNotShowAgain";

        static EssentialManifestUpdateChecker()
        {
            EditorApplication.delayCall += CheckOnceAfterLoad;
        }

        static async void CheckOnceAfterLoad()
        {
            EditorApplication.delayCall -= CheckOnceAfterLoad;

            if (EditorPrefs.GetBool(k_DoNotShowAgainKey, false))
                return;
            if (SessionState.GetBool(k_CheckedThisSessionKey, false))
                return;
            SessionState.SetBool(k_CheckedThisSessionKey, true);

            if (!EssentialPackageManifestStore.TryLoadCached(out EssentialManifestSnapshot cachedSnapshot))
                return;

            try
            {
                EssentialManifestSnapshot remoteSnapshot = await EssentialPackageManifestStore.FetchRemoteAsync();
                if (!EssentialPackageManifestStore.HasNewerVersion(cachedSnapshot, remoteSnapshot))
                    return;

                EssentialUpdatePromptWindow.Result promptResult =
                    await EssentialUpdatePromptWindow.ShowAsync(
                        "Essential Update Available",
                        BuildMessage(cachedSnapshot.Manifest, remoteSnapshot.Manifest));

                if (promptResult.DoNotShowAgain)
                    EditorPrefs.SetBool(k_DoNotShowAgainKey, true);

                if (promptResult.ShouldUpdate)
                    await DownloadLatestUnityPackageAsync(remoteSnapshot);
            }
            catch (Exception exception)
            {
                Debug.LogWarning($"[Inworld] Failed to check Essential manifest updates: {exception.Message}");
            }
        }

        static string BuildMessage(EssentialPackageManifest cachedManifest, EssentialPackageManifest remoteManifest)
        {
            string currentVersion = string.IsNullOrWhiteSpace(cachedManifest?.VersionLabel)
                ? "cached local manifest"
                : cachedManifest.VersionLabel;
            string latestVersion = string.IsNullOrWhiteSpace(remoteManifest?.VersionLabel)
                ? "latest cloud manifest"
                : remoteManifest.VersionLabel;

            return
                $"A newer Essential dependency manifest is available.\n\nCurrent: {currentVersion}\nLatest: {latestVersion}\n\n" +
                "Click Update to download and import the latest unitypackage now.";
        }

        static async System.Threading.Tasks.Task DownloadLatestUnityPackageAsync(EssentialManifestSnapshot remoteSnapshot)
        {
            string packageUrl = string.IsNullOrWhiteSpace(remoteSnapshot.Manifest?.UpdatePackageUrl)
                ? remoteSnapshot.Manifest?.PackageUrl
                : remoteSnapshot.Manifest.UpdatePackageUrl;
            string displayName = string.IsNullOrWhiteSpace(remoteSnapshot.Manifest?.UpdatePackageUrl)
                ? remoteSnapshot.Manifest?.PackageDisplayName
                : remoteSnapshot.Manifest.UpdatePackageDisplayName;

            if (string.IsNullOrWhiteSpace(packageUrl))
            {
                Debug.LogWarning("[Inworld] Remote Essential manifest does not contain a valid unitypackage URL.");
                return;
            }

            Debug.Log($"[Inworld] Update download URL: {packageUrl}");
            bool importSucceeded = await DependencyDownloaderWindow.DownloadAndImportUnityPackage(packageUrl, displayName);
            if (importSucceeded)
                EssentialPackageManifestStore.SaveCache(remoteSnapshot);
        }
    }

    internal sealed class EssentialUpdatePromptWindow : EditorWindow
    {
        internal readonly struct Result
        {
            public Result(bool shouldUpdate, bool doNotShowAgain)
            {
                ShouldUpdate = shouldUpdate;
                DoNotShowAgain = doNotShowAgain;
            }

            public bool ShouldUpdate { get; }
            public bool DoNotShowAgain { get; }
        }

        string m_Message;
        bool m_DoNotShowAgain;
        TaskCompletionSource<Result> m_ResultSource;
        bool m_HasResolved;

        internal static Task<Result> ShowAsync(string title, string message)
        {
            EssentialUpdatePromptWindow window = CreateInstance<EssentialUpdatePromptWindow>();
            window.titleContent = new GUIContent(title);
            window.m_Message = message;
            window.m_ResultSource = new TaskCompletionSource<Result>();
            window.minSize = new Vector2(460, 180);
            window.maxSize = new Vector2(460, 240);
            window.ShowUtility();
            return window.m_ResultSource.Task;
        }

        void OnGUI()
        {
            EditorGUILayout.Space(10);
            EditorGUILayout.LabelField("Essential Update Available", EditorStyles.boldLabel);
            EditorGUILayout.Space(6);
            EditorGUILayout.HelpBox(m_Message ?? string.Empty, MessageType.Info);
            EditorGUILayout.Space(8);

            m_DoNotShowAgain = EditorGUILayout.ToggleLeft("Do not show again", m_DoNotShowAgain);

            EditorGUILayout.Space(10);
            using (new EditorGUILayout.HorizontalScope())
            {
                GUILayout.FlexibleSpace();
                if (GUILayout.Button("Update", GUILayout.Width(100), GUILayout.Height(28)))
                    ResolveAndClose(true);
                if (GUILayout.Button("Later", GUILayout.Width(100), GUILayout.Height(28)))
                    ResolveAndClose(false);
            }
        }

        void OnDestroy()
        {
            if (!m_HasResolved && m_ResultSource != null)
                m_ResultSource.TrySetResult(new Result(false, m_DoNotShowAgain));
        }

        void ResolveAndClose(bool shouldUpdate)
        {
            if (m_HasResolved)
                return;

            m_HasResolved = true;
            m_ResultSource?.TrySetResult(new Result(shouldUpdate, m_DoNotShowAgain));
            Close();
        }
    }
}
#endif
