using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

namespace Davanci.Nexus
{
    // First scene of every build. Plays the splash, asks for every permission (CMP → ATT →
    // notifications) once the brand has been shown, and opens the game (build index 1, loaded
    // in the background meanwhile): on first launch once every prompt is answered, on return
    // visits as soon as the intro has played.
    public sealed class NexusSplashController : MonoBehaviour
    {
        [SerializeField] CanvasGroup canvasGroup;
        [SerializeField] Image logoImage;
        [SerializeField] Slider progressBar;
        [Tooltip("Code-driven motion. Leave empty to fall back to a plain fade.")]
        [SerializeField] NexusSplashFx fx;

        [Header("Timing (seconds)")]
        [SerializeField] float fadeInSeconds = 0.45f;
        [SerializeField] float fadeOutSeconds = 0.55f;
        [SerializeField] float returnVisitSeconds = 1.5f;

        bool _customLogo;
        bool _returnVisit;
        AsyncOperation _nextScene;
        ThreadPriority _loadingPriority;

        // True while a splash is on screen: AuthorizationManager then leaves starting the
        // permission flow to it instead of starting it on its own.
        public static bool OwnsPermissions { get; private set; }

        // The intro always plays out in full, even when startup is already done.
        bool IntroDone => fx == null || fx.IntroDone;

        // The game scene has loaded in the background and only waits to be activated.
        bool NextSceneReady => _nextScene == null || _nextScene.progress >= 0.9f;

        bool ReadyToLeave => PermissionsAnswered && IntroDone && NextSceneReady;

        // The game loads only after every prompt is answered. Startup never completes when a game
        // skips Nexus init (editor, tests, automation), so only wait for it while Nexus is running.
        static bool PermissionsAnswered =>
            AuthorizationManager.Instance == null || NexusStartup.IsStartupComplete;

        void Awake()
        {
            OwnsPermissions = true;
            _returnVisit = NexusStartup.HasCompletedPermissionsFlow;
            ApplyLogoOverride();
            if (fx == null && canvasGroup != null) canvasGroup.alpha = 0f;
            SetProgress(0f);
        }

        void Start()
        {
            PreloadNextScene();
            StartCoroutine(RunSplash());
            StartCoroutine(BeginPermissionsWhenReady());
        }

        // Loads the game scene while the splash plays, so leaving it is only an activation. The
        // load runs at a gentle priority during the intro to keep the animation smooth.
        void PreloadNextScene()
        {
            if (SceneManager.sceneCountInBuildSettings <= 1) return;
            _loadingPriority = Application.backgroundLoadingPriority;
            Application.backgroundLoadingPriority = ThreadPriority.BelowNormal;
            _nextScene = SceneManager.LoadSceneAsync(1, LoadSceneMode.Single);
            _nextScene.allowSceneActivation = false;
        }

        // Once the intro has played, finish any remaining load as fast as possible.
        void LoadFasterOnceIntroDone()
        {
            if (_nextScene != null && IntroDone)
                Application.backgroundLoadingPriority = ThreadPriority.High;
        }

        void OnDestroy() => OwnsPermissions = false;

        // First launch: prompts appear once "DAVANCI NEXUS" is half written; the flow itself waits
        // for the app to be active before each prompt. Return visits have no prompts to show, so
        // the flow starts right away and finishes in the background, even after the game opens.
        IEnumerator BeginPermissionsWhenReady()
        {
            while (!_returnVisit && fx != null && !fx.ReadyForPrompts)
                yield return null;
            BeginPermissions();
        }

        static void BeginPermissions()
        {
            var auth = AuthorizationManager.Instance;
            if (auth != null) auth.BeginPermissions();
        }

        IEnumerator RunSplash()
        {
            if (fx != null) fx.Play(wireframe: !_customLogo, _returnVisit);
            else yield return Fade(0f, 1f, fadeInSeconds);

            if (!_returnVisit)
            {
                // First launch: ease the bar toward 92% until startup actually completes.
                float p = 0f;
                while (!ReadyToLeave)
                {
                    LoadFasterOnceIntroDone();
                    p += (0.92f - p) * Mathf.Clamp01(Time.unscaledDeltaTime * 1.2f);
                    SetProgress(p);
                    yield return null;
                }
            }
            else
            {
                // Return visit: play the quicker intro and go straight in; startup keeps running
                // in the background (AuthorizationManager survives the scene change).
                var cfg = NexusConfig.Load();
                float hold = cfg != null ? Mathf.Max(0.5f, cfg.splashReturnMinSeconds) : returnVisitSeconds;
                float start = Time.unscaledTime;
                while (Time.unscaledTime < start + hold || !IntroDone || !NextSceneReady)
                {
                    LoadFasterOnceIntroDone();
                    float timed = (Time.unscaledTime - start) / hold;
                    SetProgress(fx != null ? Mathf.Min(timed, fx.IntroProgress) : timed);
                    yield return null;
                }
            }

            yield return FillTo(1f, 0.25f);
            if (fx != null) yield return fx.Outro();
            else yield return Fade(canvasGroup != null ? canvasGroup.alpha : 1f, 0f, fadeOutSeconds);
            LoadNextScene();
        }

        // NexusConfig.splashLogo replaces the Davanci cube; the cube's wireframe intro is then skipped.
        void ApplyLogoOverride()
        {
            var cfg = NexusConfig.Load();
            if (cfg == null || cfg.splashLogo == null || logoImage == null) return;
            logoImage.sprite = cfg.splashLogo;
            _customLogo = true;
        }

        void SetProgress(float p)
        {
            if (fx != null) fx.SetProgress(p);
            else if (progressBar != null) progressBar.value = Mathf.Clamp01(p);
        }

        IEnumerator FillTo(float target, float duration)
        {
            if (fx != null)
            {
                fx.SetProgress(target);
                float until = Time.unscaledTime + duration + 0.2f;
                while (fx.ShownProgress < target - 0.01f && Time.unscaledTime < until)
                    yield return null;
                yield break;
            }

            if (progressBar == null) yield break;
            float from = progressBar.value, start = Time.unscaledTime;
            duration = Mathf.Max(0.01f, duration);
            for (float t = 0f; t < 1f;)
            {
                t = Mathf.Clamp01((Time.unscaledTime - start) / duration);
                SetProgress(Mathf.Lerp(from, target, t));
                yield return null;
            }
        }

        IEnumerator Fade(float from, float to, float duration)
        {
            if (canvasGroup == null) yield break;
            duration = Mathf.Max(0.01f, duration);
            float start = Time.unscaledTime;
            canvasGroup.alpha = from;
            for (float t = 0f; t < 1f;)
            {
                t = Mathf.Clamp01((Time.unscaledTime - start) / duration);
                canvasGroup.alpha = Mathf.Lerp(from, to, t * t * (3f - 2f * t)); // smoothstep
                yield return null;
            }
            canvasGroup.alpha = to;
        }

        void LoadNextScene()
        {
            if (_nextScene == null)
            {
                Debug.LogError("[Nexus] No scene at build index 1. Put NexusSplash at 0 and your game scene at 1 in Build Settings.");
                return;
            }
            Application.backgroundLoadingPriority = _loadingPriority;
            _nextScene.allowSceneActivation = true;
        }
    }
}
