using System.Collections.Generic;
using UnityEngine;

public class PoolManager : IManager
{
    #region Pool
    /// <summary>
    /// 개별 오브젝트 풀.
    /// </summary>
    class Pool
    {
        // 이 풀이 관리하는 오브젝트의 원본 프리팹 경로
        private string _path;
        // 이 풀의 오브젝트들을 담아둘 부모 Transform
        public Transform Root { get; private set; }

        // 비활성화된 오브젝트(재고)를 담아두는 스택
        private Stack<GameObject> _poolStack = new Stack<GameObject>();

        /// <summary>
        /// 풀 초기화
        /// </summary>
        public void Init(string path, int count)
        {
            _path = path;
            GameObject original = Manager.Resource.Load<GameObject>(_path);
            if(original == null)
            {
                Debug.LogError($"Pool Init Failed! Original prefab not found at path: {_path}");
                return;
            }

            Root = new GameObject() { name = $"{original.name}_Root" }.transform;

            for (int i = 0; i < count; i++)
            {
                Push(Create(original));
            }
        }

        /// <summary>
        /// 새 오브젝트 생성. Poolable 컴포넌트를 추가하고 경로를 지정.
        /// </summary>
        private GameObject Create(GameObject original)
        {
            GameObject go = Object.Instantiate(original);
            go.name = original.name;
            go.AddComponent<Poolable>().Path = _path;
            return go;
        }

        /// <summary>
        /// 다 쓴 오브젝트를 풀에 반납 (비활성화 후 스택에 추가)
        /// </summary>
        public void Push(GameObject go)
        {
            if (go == null) return;

            go.transform.SetParent(Root);
            go.SetActive(false);
            _poolStack.Push(go);
        }

        /// <summary>
        /// 풀에서 오브젝트를 꺼냄 (재고가 있으면 재고 사용, 없으면 새로 생성)
        /// </summary>
        public GameObject Pop(Transform parent)
        {
            GameObject go;

            if (_poolStack.Count > 0)
            {
                go = _poolStack.Pop();
            }
            else
            {
                // 풀이 비었을 경우, 새로 5개를 생성하여 풀에 추가합니다.
                GameObject original = Manager.Resource.Load<GameObject>(_path);
                for (int i = 0; i < 5; i++)
                {
                    Push(Create(original));
                }
                // 이제 풀이 채워졌으니, 하나를 꺼내서 사용합니다.
                go = _poolStack.Pop();
            }

            go.SetActive(true);
            
            
            // @DontDestroyOnLoad 해제 부
            // if(parent == null)
            //     poolable.transform.parent = Managers.Scene.CurrentScene.transform;
            
            go.transform.SetParent(parent);

            return go;
        }
    }
    #endregion

    // 모든 풀들을 관리하는 딕셔너리. <프리팹 경로, 풀>
    private Dictionary<string, Pool> _pools = new Dictionary<string, Pool>();
    private Transform _root;

    /// <summary>
    /// PoolManager 초기화. @Pool_Root 오브젝트 생성.
    /// </summary>
    public void Init()
    {
        if (_root == null)
        {
            _root = new GameObject { name = "@Pool_Root" }.transform;
            Object.DontDestroyOnLoad(_root);
        }
    }

    /// <summary>
    /// 게임 시작 시 미리 풀을 생성해 둠.
    /// </summary>
    public void CreatePool(string path, int count = 5)
    {
        if (_pools.ContainsKey(path))
        {
            Debug.LogWarning($"Pool already exists for path: {path}");
            return;
        }

        Pool pool = new Pool();
        pool.Init(path, count);
        pool.Root.SetParent(_root);
        _pools.Add(path, pool);
    }

    /// <summary>
    /// 풀에서 오브젝트를 꺼내 사용.
    /// </summary>
    public GameObject Pop(string path, Transform parent = null)
    {
        // 요청한 풀이 없으면 새로 1개짜리 풀을 생성
        if (!_pools.ContainsKey(path))
        {
            CreatePool(path, 1);
        }
        
        return _pools[path].Pop(parent);
    }

    /// <summary>
    /// 사용이 끝난 오브젝트를 풀에 반납.
    /// </summary>
    public void Push(GameObject go)
    {
        // 오브젝트의 Poolable 컴포넌트에서 원본 경로를 찾아냄
        string path = go.GetComponent<Poolable>()?.Path;
        if (string.IsNullOrEmpty(path) || !_pools.ContainsKey(path))
        {
            Debug.LogWarning("Push failed! Check if object is poolable or if pool exists.");
            Object.Destroy(go);
            return;
        }

        // 경로에 맞는 풀에 오브젝트를 반납
        _pools[path].Push(go);
    }

    /// <summary>
    /// 모든 풀의 오브젝트들을 파괴하고 풀 목록을 초기화.
    /// </summary>
    public void Clear()
    {
        foreach (Transform child in _root)
        {
            GameObject.Destroy(child.gameObject);
        }
        _pools.Clear();
    }
}