using System;
using System.Collections.Generic;
using UnityEngine;
using Zenject;

namespace FlappyBird.Core
{
    public class ObjectPoolManager : IObjectPoolManager, IInitializable
    {
        private readonly DiContainer _container;

        private readonly Dictionary<Type, Queue<Component>> _pools = new Dictionary<Type, Queue<Component>>();
        private readonly Dictionary<Type, GameObject> _prefabs = new Dictionary<Type, GameObject>();
        private readonly Dictionary<Type, Transform> _poolRoots = new Dictionary<Type, Transform>();

        private Transform _mainPoolRoot;

        [Inject]
        public ObjectPoolManager(DiContainer container)
        {
            _container = container;
        }

        /// <summary>
        /// Initializes the main root transform to keep the scene hierarchy clean.
        /// </summary>
        public void Initialize()
        {
            _mainPoolRoot = new GameObject("ObjectPools").transform;
            UnityEngine.Object.DontDestroyOnLoad(_mainPoolRoot);
        }

        public void RegisterPool<T>(GameObject prefab, int initialSize = 0) where T : Component, IPoolable
        {
            Type type = typeof(T);

            if (_pools.ContainsKey(type))
            {
                Debug.LogWarning($"[ObjectPoolManager] Pool for type {type.Name} is already registered.");
                return;
            }

            _prefabs[type] = prefab;
            _pools[type] = new Queue<Component>();

            // Create a specific parent transform for this type
            GameObject rootGo = new GameObject($"{type.Name}_Pool");

            // Check if prefab is a UI element (RectTransform)
            if (prefab.transform is RectTransform)
            {
                // UI elements need to stay under a Canvas even when pooled, 
                // or be moved to a hidden Canvas to stay "initialized"
                rootGo.AddComponent<RectTransform>();
            }

            Transform typeRoot = rootGo.transform;
            typeRoot.SetParent(_mainPoolRoot);
            _poolRoots[type] = typeRoot;

            // Pre-warm the pool
            for (int i = 0; i < initialSize; i++)
            {
                T instance = CreateInstance<T>();
                instance.gameObject.SetActive(false);
                _pools[type].Enqueue(instance);
            }
        }

        public T Get<T>() where T : Component, IPoolable
        {
            Type type = typeof(T);

            if (!_pools.TryGetValue(type, out Queue<Component> pool))
            {
                throw new InvalidOperationException($"Pool for type {type.Name} is not registered.");
            }

            T instance = (pool.Count == 0) ? CreateInstance<T>() : pool.Dequeue() as T;

            instance.gameObject.SetActive(true);
            instance.OnSpawned();

            return instance;
        }

        /// <summary>
        /// Returns an item to its respective pool. 
        /// Automatically handles UI parenting back to the pool root.
        /// </summary>
        public void Return<T>(T item) where T : Component, IPoolable
        {
            if (item == null) return;

            Type type = typeof(T);

            if (!_pools.TryGetValue(type, out Queue<Component> pool))
            {
                // Fallback for cases where T might be an interface or base class
                type = item.GetType();
                if (!_pools.TryGetValue(type, out pool))
                {
                    Debug.LogError($"[ObjectPoolManager] No pool registered for {type.Name}");
                    return;
                }
            }

            item.OnDespawned();

            // Re-parent to the specific pool root to keep Hierarchy clean
            item.transform.SetParent(_poolRoots[type], false);
            item.gameObject.SetActive(false);

            pool.Enqueue(item);
        }

        private T CreateInstance<T>() where T : Component, IPoolable
        {
            Type type = typeof(T);
            GameObject prefab = _prefabs[type];
            Transform root = _poolRoots[type];

            // Zenject InstantiatePrefab ensures [Inject] works inside the prefab (needed for ScorePopupView)
            GameObject instance = _container.InstantiatePrefab(prefab, root);
            T component = instance.GetComponent<T>();

            if (component == null)
            {
                component = instance.AddComponent<T>();
            }

            return component;
        }
    }
}