using System;
using System.Collections;
using System.Collections.Generic;
using UDB;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;

public class NetworkSceneDirector : SingletonBehaviour<NetworkSceneDirector>
{
    #region Fields/Properties
    private UDBTask loadSingleSceneTask;
    private bool isLoading;
    private bool isUnloading;
    public Action OnWillLoadScene;
    #endregion

    #region Unity Methods
    void Start()
    {
        NetworkManager.Singleton.OnServerStarted += OnServerStarted;
    }

    protected override void OnDestroy()
    {
        if (NetworkManager.Singleton != null) {
            NetworkManager.Singleton.OnServerStarted -= OnServerStarted;
        }
        base.OnDestroy();
    }
    #endregion

    #region Public Methods
    public void LoadSingleScene(string sceneName, Action callback = null)
    {
        loadSingleSceneTask = UDBTask.NewTask(DoLoadSingleScene(sceneName, callback));
    }

    //Since the NetworkManager has no action for when the networkmanager is ready
    //and before we syncronize we need to call this manually when connecting as a client
    public void OnClientStarted(bool shouldCleanUp = false)
    {
        NetworkManager.Singleton.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive);
        if (shouldCleanUp) {
            for (int n = 0; n < SceneManager.sceneCount; ++n) {
                Scene scene = SceneManager.GetSceneAt(n);
                if (scene.name != UDB.Constants.coreScene && scene.name != "NetworkCore") {
                    SceneManager.UnloadSceneAsync(scene);
                }
            }
        }
    }
    #endregion

    #region Private
    void OnServerStarted()
    {
        NetworkManager.Singleton.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive);
        NetworkManager.Singleton.SceneManager.LoadScene("NetworkCore", LoadSceneMode.Additive);
        NetworkManager.Singleton.SceneManager.OnLoad += OnLoadDelegateHandler;
        NetworkManager.Singleton.SceneManager.OnLoadComplete += OnLoadCompleteDelegateHandler;
        NetworkManager.Singleton.SceneManager.OnUnload += OnUnloadDelegateHandler;
        NetworkManager.Singleton.SceneManager.OnUnloadComplete += OnUnloadCompleteDelegateHandler;
    }

    void OnLoadDelegateHandler(ulong clientId, string sceneName, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation)
    {
        isLoading = true;
    }

    void OnLoadCompleteDelegateHandler(ulong clientId, string sceneName, LoadSceneMode loadSceneMode)
    {
        isLoading = false;
    }

    void OnUnloadDelegateHandler(ulong clientId, string sceneName, AsyncOperation asyncOperation)
    {
        isUnloading = true;
    }

    void OnUnloadCompleteDelegateHandler(ulong clientId, string sceneName)
    {
        isUnloading = false;
    }

    IEnumerator DoLoadSingleScene(string sceneName, Action callback)
    {
        OnWillLoadScene?.Invoke();
        //For some reason when hosting the load lobby scene can happen too quickly which causes the loading screen to get stuck
        //so lets wait 1 second here to let the loading screen appear fully
        yield return new WaitForSeconds(1f);
        //just incase we are still doing a previous load since the network scene manager can only do one scene action at a time
        while (isLoading) {
            yield return null;
        }
        while (isUnloading) {
            yield return null;
        }

        //Unload all scenes excpect core
        Queue<Scene> scenesToUnload = new Queue<Scene>();
        for (int n = 0; n < SceneManager.sceneCount; ++n) {
            Scene scene = SceneManager.GetSceneAt(n);
            if (scene.name != UDB.Constants.coreScene && scene.name != "NetworkCore") {
                scenesToUnload.Enqueue(scene);
            }
        }
        while (scenesToUnload.Count > 0) {
            Scene scene = scenesToUnload.Dequeue();
            if (scene.name == "MainMenu") {
                SceneManager.UnloadSceneAsync(scene);
            } else {
                NetworkManager.Singleton.SceneManager?.UnloadScene(scene);
            }
            yield return null;
            while (isUnloading) {
                yield return null;
            }
        }

        NetworkManager.Singleton.SceneManager?.LoadScene(sceneName, LoadSceneMode.Additive);
        yield return null;

        while (isLoading) {
            yield return null;
        }

        if (callback != null) {
            callback();
        }
    }
    #endregion
}
