using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.SceneManagement;
using UnityEngine.Analytics;
using CustomAssetBundleSystem;

/*
    DESCRIPTION:
    1. When the Loading_Screen gameobject with this script is enabled, the loading slider animates to show progress when changing
        between UI's or scenes

*/


public class LoadingScreen : MonoBehaviour
{
    public GameObject BackgroundImg_GameObject; //Reference to the background image in the loading UI
    public GameObject Hints_GameObject; //Reference to Hints text in the loading UI
    public Slider loadingSlider;    //Reference to the slider in the loading UI

    //For the "Loading..." text shown
    public TextMeshProUGUI loadingText;
    public string loadingBaseText; //Text showing "Loading..." whereas the dots appear to animate
    public string og_text;

    public string ui_to_show;
    public int to_change_to; //1. Scene, 2. UI

    public Sprite[] backgroundImages; // Array of background images
    public string[] hintsArray; //Array of hints to be displayed while loading

    private int maxBackgroundChanges = 1, backgroundChangeCount = 0; // Maximum number of background changes
    private int maxhintsChanges = 1, hintsChangeCount = 0; // Counter to keep track of background changes
    private int maxLoaderChanges = 3, loaderChangeCount = 0; // Counter to keep track of background changes
    
    bool flag = true; //flag to check if loading has been initialized
    private GameManager gameManager;
    private float barProgess;
    private bool pendingInitialization;
    private string targetSceneName; // remember scene we are loading so we can react on load
    private bool sceneHideHookRegistered;
    private bool fallbackHideHookRegistered; // subscribed when we didn't initialize via to_change_to==1

    // Scenes on which we should auto-hide the loading UI even if we didn't initiate the load via this component.
    // Keeps prior behavior intact while fixing cases like WaitingSceneManager.
    [SerializeField]
    private string[] autoHideOnSceneNames = new[] { "Game" };

    // AssetBundle Download Integration
    [Header("AssetBundle Download Settings")]
    [SerializeField] private bool enableAssetBundleDownload = false; // DISABLED: Using Build Settings scenes instead
    [SerializeField] private bool downloadBeforeMenu = false; // DISABLED: Using Build Settings scenes instead
    private AssetBundleDownloadManager downloadManager;

    // Force disable AssetBundle system at runtime (overrides serialized Inspector values)
    private void Awake()
    {
        // IMPORTANT: Force disable AssetBundle downloading until you want to re-enable it
        enableAssetBundleDownload = false;
        downloadBeforeMenu = false;
    }


    private void OnEnable()
    {
        gameManager = GameManager.Instance;
        if (gameManager == null)
        {
            gameManager = GetComponent<GameManager>() ?? GetComponentInParent<GameManager>();
        }
    if (gameManager == null)
    {
#if UNITY_2022_2_OR_NEWER
        gameManager = FindFirstObjectByType<GameManager>();
#else
        gameManager = FindObjectOfType<GameManager>();
#endif
    }

        // Initialize AssetBundle download manager
        if (enableAssetBundleDownload && downloadManager == null)
        {
            downloadManager = gameObject.AddComponent<AssetBundleDownloadManager>();
            
            // Pass UI references to download manager
            var downloadManagerType = downloadManager.GetType();
            var sliderField = downloadManagerType.GetField("loadingSlider", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            var loadingTextField = downloadManagerType.GetField("loadingText", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            var hintsTextField = downloadManagerType.GetField("hintsText", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            
            if (sliderField != null) sliderField.SetValue(downloadManager, loadingSlider);
            if (loadingTextField != null) loadingTextField.SetValue(downloadManager, loadingText);
            if (hintsTextField != null) hintsTextField.SetValue(downloadManager, Hints_GameObject.GetComponent<TextMeshProUGUI>());
        }

        // Hints to be shown  
        hintsArray = new string[]
        {
            "Complete Weekly Missions to gain XP",
            "Complete a 20 Wins streak to level up in Rank",
            "Missions change every Week"
        };

        og_text = loadingText.text;  // "Loading..." text
        loadingBaseText = loadingText.text;

        if (pendingInitialization)
        {
            InitializeLoading();
        }

        // Always attach a lightweight fallback so if another system loads the scene (e.g., WaitingSceneManager),
        // we still hide the loading UI when the gameplay scene is ready.
        if (!fallbackHideHookRegistered)
        {
            SceneManager.sceneLoaded += FallbackSceneLoadedHide;
            fallbackHideHookRegistered = true;
        }
    }

    private void OnDisable()
    {
        StopAllCoroutines();
        // Ensure we unsubscribe any scene hooks to avoid leaks.
        if (sceneHideHookRegistered)
        {
            SceneManager.sceneLoaded -= HandleSceneLoadedInternal;
            sceneHideHookRegistered = false;
        }
        if (fallbackHideHookRegistered)
        {
            SceneManager.sceneLoaded -= FallbackSceneLoadedHide;
            fallbackHideHookRegistered = false;
        }
    }

    // Function called when loading has been initialized
    public void InitializeLoading()
    {
        pendingInitialization = false;
        StopAllCoroutines();
        backgroundChangeCount = 0;
        hintsChangeCount = 0;
        loaderChangeCount = 0;
        flag = true;

        if (loadingSlider != null)
        {
            loadingSlider.value = 0f;
        }

        if (!string.IsNullOrEmpty(og_text) && loadingText != null)
        {
            loadingBaseText = og_text;
            loadingText.text = og_text;
        }

        ChangeBackgroundImage();
        ChangeHintsText();
        StartCoroutine(ChangeBackgroundWithDelay());
        StartCoroutine(ChangeHintsWithDelay());

        // Check if we should download AssetBundles BEFORE loading menu
        if (enableAssetBundleDownload && downloadBeforeMenu && ui_to_show == "MenuScene" && to_change_to == 1)
        {
            Debug.Log("[LoadingScreen] Starting AssetBundle download before MenuScene...");
            StartCoroutine(DownloadThenLoadScene(ui_to_show));
            return; // Don't proceed with normal loading yet
        }

        if (to_change_to == 1)
        {
            if (GameManager.Instance != null)
            {
                SceneManager.sceneLoaded += GameManager.Instance.OnSceneLoaded;
            }
            targetSceneName = ui_to_show;
            // Also register a local handler to guarantee the loading UI hides even if external code path changes.
            if (!sceneHideHookRegistered)
            {
                SceneManager.sceneLoaded += HandleSceneLoadedInternal;
                sceneHideHookRegistered = true;
            }
            StartCoroutine(LoadSceneAsync(ui_to_show));
        }
        else
        {
            StartCoroutine(AnimateSliderFill(5f));//duration should be 3 for editor simulation, 5 for mobile apk lower than that the corouting gets jammed
        }
    }

    /// <summary>
    /// Download AssetBundles first, then load the target scene
    /// </summary>
    private IEnumerator DownloadThenLoadScene(string sceneName)
    {
        if (downloadManager == null)
        {
            Debug.LogError("[LoadingScreen] DownloadManager not initialized!");
            // Fallback to normal loading
            StartCoroutine(LoadSceneAsync(sceneName));
            yield break;
        }

        Debug.Log("[LoadingScreen] Starting AssetBundle download process...");

        bool downloadSuccess = false;

        // Start download with callback
        yield return downloadManager.StartDownloadProcess((success) =>
        {
            downloadSuccess = success;
        });

        if (downloadSuccess)
        {
            Debug.Log("[LoadingScreen] ✓ AssetBundle download complete! Loading scene...");
            
            // Now proceed with scene loading
            if (GameManager.Instance != null)
            {
                SceneManager.sceneLoaded += GameManager.Instance.OnSceneLoaded;
            }
            targetSceneName = sceneName;
            if (!sceneHideHookRegistered)
            {
                SceneManager.sceneLoaded += HandleSceneLoadedInternal;
                sceneHideHookRegistered = true;
            }
            
            yield return LoadSceneAsync(sceneName);
        }
        else
        {
            Debug.LogError("[LoadingScreen] ✗ AssetBundle download failed! Cannot proceed.");
            
            // Show error message - user must retry or have internet
            if (loadingText != null)
            {
                loadingText.text = "Download Failed - Check Internet Connection";
            }
            if (Hints_GameObject != null)
            {
                Hints_GameObject.GetComponent<TextMeshProUGUI>().text = "Please connect to the internet and restart the game";
            }
            
            // Don't proceed to menu - block here
        }
    }


    // Function to animate the loading slider being filled from empty to full
    private IEnumerator AnimateSliderFill(float duration)
    {
        loadingBaseText = "LOADING";
        int dotsCount = 3;

        float timer = 0f;
        float startValue = loadingSlider.value;
        float targetValue = 1f; // Assuming the slider's range is 0 to 1
        float fakeProgress = 0;
        loadingSlider.value = 0.01f;
        while (timer < duration)
        {
            if (fakeProgress >= 0.25f && fakeProgress < 0.26f)
            {
                yield return new WaitForSeconds(0.15f); // Set a delay of 0.5 seconds
            }

            if (fakeProgress >= 0.75f && fakeProgress < 0.76f)
            {
                yield return new WaitForSeconds(0.25f); // Set a delay of 0.5 seconds
            }

            fakeProgress = Mathf.Lerp(startValue, targetValue, timer / duration);

            if (loadingSlider != null)
            {
                loadingSlider.value = fakeProgress;
            }

            //Create ... animation effect on the Loading... text
            int dotsToShow = Mathf.FloorToInt(timer % (dotsCount + 1));
            string dots = new string('.', dotsToShow);
            loadingText.text = loadingBaseText + dots;

            timer += Time.deltaTime;

            if ((loaderChangeCount < maxLoaderChanges) && (fakeProgress >= 0.01f && fakeProgress < 0.4f ||
                               fakeProgress >= 0.5f && fakeProgress < 0.65f ||
                               fakeProgress >= 0.8f && fakeProgress < 0.9f))
            {
                float randomLoaderDelay = Mathf.Round(Random.Range(0.01f, 0.09f) * 100) / 100; // Random value between 1 and 2 with 2 decimal places
                //Debug.Log("randomdelay " + randomLoaderDelay);
                yield return new WaitForSeconds(randomLoaderDelay);
                loaderChangeCount++;
            }

            yield return null;
        }

        //loadingSlider.value = targetValue;
        loadingText.text = "LOADING."; // Reset text to the original after completion
        yield return new WaitForSeconds(0.5f);

        if (to_change_to == 2) // UI
        {
            GameObject uiObject = GameObject.Find("UI");
            GameObject UI_to_be_shown = uiObject.transform.Find(ui_to_show).gameObject;

            UI_to_be_shown.SetActive(true);
        }

    }

    // Function to load scene, and update slider value based on the loading progress
    private IEnumerator LoadSceneAsync(string sceneName)
    {
        // Start a guarded async scene load (Single mode) and prevent duplicates
        SceneLoader.SceneLoadHandle asyncLoad = SceneLoader.LoadSceneAsyncOnce(sceneName, LoadSceneMode.Single, allowSceneActivation: false);

        // If a different scene is already loading, try to reuse the current operation if available
        if (asyncLoad == null)
        {
            asyncLoad = SceneLoader.CurrentOperation;
            if (asyncLoad == null)
                yield break; // Nothing to wait on
        }

        if (gameManager != null)
        {
            gameManager.ShowLoadingUI();
        }
        yield return new WaitForSeconds(2f);
        float timer = 0f;
        float previousValue = 0;
        while (!asyncLoad.isDone)
        {
            if (loadingSlider.value >= 0.25f && loadingSlider.value < 0.26f)
            {
                yield return new WaitForSeconds(0.15f); // Set a delay of 0.5 seconds
            }

            if (loadingSlider.value >= 0.75f && loadingSlider.value < 0.76f)
            {
                yield return new WaitForSeconds(0.25f); // Set a delay of 0.5 seconds
            }
            if (previousValue < Mathf.Lerp(0, asyncLoad.progress, timer / 4f))
            {
                previousValue = Mathf.Lerp(0, asyncLoad.progress, timer / 4f);
                loadingSlider.value = Mathf.Lerp(0, asyncLoad.progress, timer / 4f);
            }
            else
                loadingSlider.value = previousValue;

            int dotsToShow = Mathf.FloorToInt(timer % (3 + 1));
            string dots = new string('.', dotsToShow);

            loadingText.text = loadingBaseText + dots;

            timer += Time.deltaTime;

            if ((loaderChangeCount < maxLoaderChanges) && (loadingSlider.value >= 0.01f && loadingSlider.value < 0.4f ||
                               loadingSlider.value >= 0.5f && loadingSlider.value < 0.65f ||
                               loadingSlider.value >= 0.8f && loadingSlider.value < 0.9f))
            {
                float randomLoaderDelay = Mathf.Round(Random.Range(0.01f, 0.09f) * 100) / 100; // Random value between 1 and 2 with 2 decimal places
                yield return new WaitForSeconds(randomLoaderDelay);
                loaderChangeCount++;
            }
            if (loadingSlider.value >= 0.9f)
            {
                loadingText.text = "LOADING...";
                loadingSlider.value = 1.0f;
                float randomLoaderDelay = Mathf.Round(Random.Range(0.01f, 0.09f) * 100) / 100; // Random value between 1 and 2 with 2 decimal places
                yield return new WaitForSeconds(randomLoaderDelay);
                loaderChangeCount++;

                // Activate the scene once ready
                asyncLoad.allowSceneActivation = true;
                Debug.Log("[LoadingScreen] Scene activation triggered, waiting for isDone...");
            }
            yield return null;
        }
        
        Debug.Log("[LoadingScreen] Scene load complete (asyncLoad.isDone=true), hiding loading UI...");
        
        // Use GameManager.Instance singleton instead of cached reference (which may be stale after scene change)
        if (GameManager.Instance != null)
        {
            GameManager.Instance.HideLoadingUI();
        }
        else if (gameManager != null)
        {
            gameManager.HideLoadingUI();
        }
        else
        {
            Debug.LogWarning("[LoadingScreen] No GameManager available to hide loading UI!");
            // Fallback: try to disable our own root
            if (transform.root != null)
            {
                transform.root.gameObject.SetActive(false);
            }
        }
    }

    // Internal safety: ensure we always hide loading when the intended scene finishes loading.
    private void HandleSceneLoadedInternal(Scene scene, LoadSceneMode mode)
    {
        if (!string.IsNullOrEmpty(targetSceneName) && scene.name == targetSceneName)
        {
            // Stop anim coroutines.
            StopAllCoroutines();
            // Hide through GameManager if available.
            if (GameManager.Instance != null)
            {
                GameManager.Instance.HideLoadingUI();
            }
            else if (gameObject.activeInHierarchy)
            {
                // Fallback: disable our root canvas (assumes parent = loadingUI)
                transform.root.gameObject.SetActive(false);
            }
            // cleanup subscription
            SceneManager.sceneLoaded -= HandleSceneLoadedInternal;
            sceneHideHookRegistered = false;
            targetSceneName = null;
        }
    }

    // Fallback hide when we didn't initiate the load, or targetSceneName wasn't set.
    private void FallbackSceneLoadedHide(Scene scene, LoadSceneMode mode)
    {
        // If InitializeLoading set a specific target, let HandleSceneLoadedInternal manage it.
        if (!string.IsNullOrEmpty(targetSceneName))
            return;

        if (autoHideOnSceneNames != null)
        {
            for (int i = 0; i < autoHideOnSceneNames.Length; i++)
            {
                var name = autoHideOnSceneNames[i];
                if (!string.IsNullOrEmpty(name) && scene.name == name)
                {
                    // Stop any visuals if they were running.
                    StopAllCoroutines();
                    if (GameManager.Instance != null)
                    {
                        GameManager.Instance.HideLoadingUI();
                    }
                    else if (gameObject.activeInHierarchy)
                    {
                        transform.root.gameObject.SetActive(false);
                    }
                    // Once done, remove fallback hook
                    SceneManager.sceneLoaded -= FallbackSceneLoadedHide;
                    fallbackHideHookRegistered = false;
                    break;
                }
            }
        }
    }


    // Function to change the background image of the loading UI
    private IEnumerator ChangeBackgroundWithDelay()
    {
        while (true)
        {
            if (backgroundChangeCount < maxBackgroundChanges)
            {
                float randomDelay = Mathf.Round(Random.Range(1f, 2f) * 100) / 100; // Random value between 1 and 2 with 2 decimal places
                yield return new WaitForSeconds(randomDelay); // Adjust the time to wait as needed (2 seconds in this case)
                ChangeBackgroundImage();
                backgroundChangeCount++;
                
            }
            else
            {
                yield break; // Exit coroutine when maximum number of background changes is reached
            }
            yield return null;
        }
    }


    // Function to change the hints being displayed in the loading UI while the player waits the loading to be completed
    private IEnumerator ChangeHintsWithDelay()
    {
        while (true)
        {
            if (hintsChangeCount < maxhintsChanges)
            {
                float randomDelay = Mathf.Round(Random.Range(0.75f, 2.25f) * 100) / 100; // Random value between 1 and 2 with 2 decimal places
                yield return new WaitForSeconds(randomDelay); // Adjust the time to wait as needed (2 seconds in this case)
                ChangeHintsText();
                hintsChangeCount++;
            }
            else
            {
                yield break; // Exit coroutine when maximum number of background changes is reached
            }
            yield return null;
        }
    }

    // Function to change the hints text being displayed
    private void ChangeHintsText()
    {
        // Remove stray literal text assignment; pick a random hint safely.
        if (hintsArray.Length > 0)
        {
            int randomIndex = Random.Range(0, hintsArray.Length);
            Hints_GameObject.GetComponent<TextMeshProUGUI>().text = hintsArray[randomIndex];
        }
    }

    // Function to change the background image in the loading UI
    private void ChangeBackgroundImage()
    {
        if (backgroundImages.Length > 0)
        {
            int randomIndex = Random.Range(0, backgroundImages.Length);
            BackgroundImg_GameObject.GetComponent<Image>().sprite = backgroundImages[randomIndex];
        }
    }

    // Function that is referenced/called in other script which specifies what Scene or UI to change to after the loading plays out
    public void UI_to_show_AfterLoading(int x, string ui_name)
    {
        to_change_to = x;
        ui_to_show = ui_name;

        backgroundChangeCount = 0;
        hintsChangeCount = 0;
        loaderChangeCount = 0;
        flag = true;
        pendingInitialization = true;

        if (isActiveAndEnabled)
        {
            InitializeLoading();
        }
    }
}
