// Assets/_Project/Scripts/Gameplay/PlayerBuffManager.cs
using System.Collections.Generic;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using TD.Core;

namespace TD.Gameplay
{
    /// <summary>
    /// Holds all buffs currently owned by one player. Lives on the Player prefab
    /// alongside <see cref="PlayerGoldManager"/> and <see cref="PlayerMatchState"/>.
    /// </summary>
    /// <remarks>
    /// <para><b>Purchase flow.</b> The owning client calls
    /// <see cref="RequestPurchaseBuffRpc"/> with a category index. The server
    /// validates gold, deducts the cost, picks a random
    /// <see cref="BuffDefinition"/> from the category's pool, and appends an
    /// <see cref="ActiveBuff"/> to the replicated <see cref="buffs"/> list.</para>
    ///
    /// <para><b>Multiplier query.</b> <see cref="GetMultiplier"/> is called by
    /// <see cref="TD.Combat.TowerCombat"/> on the server each time a tower fires.
    /// It multiplies all active buffs for the requested stat together.</para>
    ///
    /// <para><b>Enable / disable.</b> Enemies can call <see cref="ServerDisableBuff"/>
    /// and <see cref="ServerEnableBuff"/> by index to temporarily suppress buffs
    /// without removing them from the list.</para>
    /// </remarks>
    public class PlayerBuffManager : NetworkBehaviour
    {
        // ----- Static registry (mirrors PlayerGoldManager pattern) -----------

        private static readonly Dictionary<ulong, PlayerBuffManager> s_byClientId
            = new Dictionary<ulong, PlayerBuffManager>();

        /// <summary>Returns the PlayerBuffManager owned by the given client, or null.</summary>
        public static PlayerBuffManager GetForClient(ulong clientId)
        {
            s_byClientId.TryGetValue(clientId, out var mgr);
            return mgr;
        }

        /// <summary>Convenience: the local client's own buff manager.</summary>
        public static PlayerBuffManager Local
        {
            get
            {
                var nm = NetworkManager.Singleton;
                if (nm == null) return null;
                return GetForClient(nm.LocalClientId);
            }
        }

        // ----- Inspector ------------------------------------------------------

        [Tooltip("Purchasable buff categories shown in the buff menu. Index in this " +
                 "array is the categoryIndex passed to RequestPurchaseBuffRpc.")]
        [SerializeField] private BuffCategory[] categories;

        private NetworkList<ActiveBuff> buffs;

        private void Awake()
        {
            buffs = new NetworkList<ActiveBuff>();
        }

        // ----- NGO lifecycle ----------------------------------------------

        public override void OnNetworkSpawn()
        {
            s_byClientId[OwnerClientId] = this;
        }

        public override void OnNetworkDespawn()
        {
            if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this)
                s_byClientId.Remove(OwnerClientId);
        }

        // ----- Public API -------------------------------------------------

        /// <summary>
        /// Returns the combined multiplier for <paramref name="stat"/> across all
        /// active buffs owned by this player. Returns 1.0 if no matching buffs
        /// are active. Safe to call from any peer.
        /// </summary>
        public float GetMultiplier(BuffStat stat)
        {
            float result = 1f;
            for (int i = 0; i < buffs.Count; i++)
            {
                var buff = buffs[i];
                if (buff.Stat == stat && buff.IsActive)
                    result *= buff.Multiplier;
            }
            return result;
        }

        /// <summary>
        /// Returns the number of buff categories available, for building the UI.
        /// </summary>
        public int CategoryCount => categories?.Length ?? 0;

        /// <summary>Returns the category at the given index, or null.</summary>
        public BuffCategory GetCategory(int index)
            => (categories != null && index >= 0 && index < categories.Length)
                ? categories[index]
                : null;

        /// <summary>Read-only access to the buff list for UI rendering.</summary>
        public NetworkList<ActiveBuff> Buffs => buffs;

        // ----- Server helpers ---------------------------------------------

        /// <summary>
        /// Server-only: disables the buff at <paramref name="index"/>.
        /// The buff remains in the list and can be re-enabled.
        /// </summary>
        public void ServerDisableBuff(int index)
        {
            if (!IsServer) return;
            if (index < 0 || index >= buffs.Count) return;

            var b = buffs[index];
            if (!b.IsActive) return;
            b.IsActive = false;
            buffs[index] = b;
        }

        /// <summary>Server-only: re-enables the buff at <paramref name="index"/>.</summary>
        public void ServerEnableBuff(int index)
        {
            if (!IsServer) return;
            if (index < 0 || index >= buffs.Count) return;

            var b = buffs[index];
            if (b.IsActive) return;
            b.IsActive = true;
            buffs[index] = b;
        }

        // ----- Purchase RPC -----------------------------------------------

        /// <summary>
        /// Owning-client entry point. Sends a request to the server to purchase
        /// a random buff from the category at <paramref name="categoryIndex"/>.
        /// The server validates gold and adds the buff if the purchase succeeds.
        /// </summary>
        [Rpc(SendTo.Server, RequireOwnership = true)]
        public void RequestPurchaseBuffRpc(int categoryIndex)
        {
            if (categories == null || categoryIndex < 0 || categoryIndex >= categories.Length)
            {
                Debug.LogWarning("[PlayerBuffManager] RequestPurchaseBuff: invalid category index.");
                return;
            }

            var category = categories[categoryIndex];
            if (category == null || category.Pool == null || category.Pool.Length == 0)
            {
                Debug.LogWarning("[PlayerBuffManager] RequestPurchaseBuff: category has no buffs.");
                return;
            }

            var goldManager = PlayerGoldManager.GetForClient(OwnerClientId);
            if (goldManager == null)
            {
                Debug.LogError("[PlayerBuffManager] RequestPurchaseBuff: no PlayerGoldManager found.");
                return;
            }

            if (goldManager.CurrentGold < category.Cost)
            {
                Debug.Log($"[PlayerBuffManager] Client {OwnerClientId} cannot afford " +
                          $"'{category.DisplayName}' (cost {category.Cost}, " +
                          $"gold {goldManager.CurrentGold}).");
                return;
            }

            goldManager.DeductGold(category.Cost);

            var def = category.Pool[Random.Range(0, category.Pool.Length)];
            buffs.Add(new ActiveBuff
            {
                Stat        = def.Stat,
                Multiplier  = def.Multiplier,
                IsActive    = true,
                DisplayName = new FixedString64Bytes(def.DisplayName ?? string.Empty),
            });

            Debug.Log($"[PlayerBuffManager] Client {OwnerClientId} purchased buff " +
                      $"'{def.DisplayName}' ({def.Stat} ×{def.Multiplier}).");
        }
    }
}
