using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneManagerService : MonoBehaviour
{
    public static SceneManagerService Instance { get; private set; }

    // Events for scene lifecycle
    public event Action<string> SceneLoadStarted;
    public event Action<string, float> SceneLoadProgress;
    public event Action<string> SceneLoadCompleted;
    public event Action<string> SceneUnloadStarted;
    public event Action<string, float> SceneUnloadProgress;
    public event Action<string> SceneUnloadCompleted;

    private readonly Dictionary<string, AsyncOperation> _activeLoads = new();
    private readonly Dictionary<string, AsyncOperation> _activeUnloads = new();

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    // Load a single scene asynchronously (additive)
    public async Task LoadSceneAsync(string sceneName, IProgress<float> progress = null)
    {
        SceneLoadStarted?.Invoke(sceneName);
        var op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
        _activeLoads[sceneName] = op;
        while (!op.isDone)
        {
            progress?.Report(op.progress);
            SceneLoadProgress?.Invoke(sceneName, op.progress);
            await Task.Yield();
        }
        _activeLoads.Remove(sceneName);
        SceneLoadCompleted?.Invoke(sceneName);
    }

    // Unload a single scene asynchronously
    public async Task UnloadSceneAsync(string sceneName, IProgress<float> progress = null)
    {
        var scene = SceneManager.GetSceneByName(sceneName);
        if (!scene.isLoaded)
        {
            Debug.LogWarning($"SceneManagerService: Attempted to unload scene '{sceneName}', but it is not loaded.");
            return;
        }
        SceneUnloadStarted?.Invoke(sceneName);
        var op = SceneManager.UnloadSceneAsync(sceneName);
        _activeUnloads[sceneName] = op;
        while (!op.isDone)
        {
            progress?.Report(op.progress);
            SceneUnloadProgress?.Invoke(sceneName, op.progress);
            await Task.Yield();
        }
        _activeUnloads.Remove(sceneName);
        SceneUnloadCompleted?.Invoke(sceneName);
    }

    // Load multiple scenes asynchronously
    public async Task LoadScenesAsync(IEnumerable<string> sceneNames, IProgress<float> progress = null)
    {
        int total = 0;
        int done = 0;
        var ops = new List<AsyncOperation>();
        foreach (var name in sceneNames)
        {
            total++;
            var op = SceneManager.LoadSceneAsync(name, LoadSceneMode.Additive);
            _activeLoads[name] = op;
            ops.Add(op);
            SceneLoadStarted?.Invoke(name);
        }
        while (done < total)
        {
            done = 0;
            float aggregate = 0f;
            foreach (var op in ops)
            {
                aggregate += op.progress;
                if (op.isDone) done++;
            }
            float avg = aggregate / total;
            progress?.Report(avg);
            foreach (var name in sceneNames)
                SceneLoadProgress?.Invoke(name, avg);
            await Task.Yield();
        }
        foreach (var name in sceneNames)
        {
            _activeLoads.Remove(name);
            SceneLoadCompleted?.Invoke(name);
        }
    }

    // Unload multiple scenes asynchronously
    public async Task UnloadScenesAsync(IEnumerable<string> sceneNames, IProgress<float> progress = null)
    {
        int total = 0;
        int done = 0;
        var ops = new List<AsyncOperation>();
        foreach (var name in sceneNames)
        {
            total++;
            var op = SceneManager.UnloadSceneAsync(name);
            _activeUnloads[name] = op;
            ops.Add(op);
            SceneUnloadStarted?.Invoke(name);
        }
        while (done < total)
        {
            done = 0;
            float aggregate = 0f;
            foreach (var op in ops)
            {
                aggregate += op.progress;
                if (op.isDone) done++;
            }
            float avg = aggregate / total;
            progress?.Report(avg);
            foreach (var name in sceneNames)
                SceneUnloadProgress?.Invoke(name, avg);
            await Task.Yield();
        }
        foreach (var name in sceneNames)
        {
            _activeUnloads.Remove(name);
            SceneUnloadCompleted?.Invoke(name);
        }
    }

    // Optionally: expose current progress for all active operations
    public float GetAggregateLoadProgress()
    {
        if (_activeLoads.Count == 0) return 1f;
        float sum = 0f;
        foreach (var op in _activeLoads.Values) sum += op.progress;
        return sum / _activeLoads.Count;
    }
    public float GetAggregateUnloadProgress()
    {
        if (_activeUnloads.Count == 0) return 1f;
        float sum = 0f;
        foreach (var op in _activeUnloads.Values) sum += op.progress;
        return sum / _activeUnloads.Count;
    }

    // Tracks the currently loaded gameplay scene (not persistent/bootstrapper)
    public string CurrentGameplayScene { get; private set; } = "AppleHillsOverworld";

    // Switches from current gameplay scene to a new one
    public async Task SwitchSceneAsync(string newSceneName, IProgress<float> progress = null)
    {
        // Load new scene
        await LoadSceneAsync(newSceneName, progress);
        // Unload previous scene (if not same)
        if (!string.IsNullOrEmpty(CurrentGameplayScene) && CurrentGameplayScene != newSceneName)
        {
            var prevScene = SceneManager.GetSceneByName(CurrentGameplayScene);
            if (prevScene.isLoaded)
            {
                await UnloadSceneAsync(CurrentGameplayScene);
            }
            else
            {
                Debug.LogWarning($"SceneManagerService: Previous scene '{CurrentGameplayScene}' is not loaded, skipping unload.");
            }
        }
        // Update tracker
        CurrentGameplayScene = newSceneName;
    }
}
