// Assets/_Project/Scripts/Gameplay/Draft/PlayerDraft.cs
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;

namespace TD.Gameplay.Draft
{
    /// <summary>
    /// Per-player draft state — the set of <see cref="DraftOption"/>s currently offered to
    /// one player. Lives on the Player prefab alongside <see cref="PlayerTowerDeck"/>,
    /// <see cref="PlayerGoldManager"/>, etc.
    /// </summary>
    /// <remarks>
    /// <para><b>Offered set.</b> <see cref="currentOptionIds"/> holds the DraftOptionIds
    /// the player is choosing between (typically 3). Empty means no active draft. The
    /// owning client renders cards from these ids; the server reads them to validate a pick.</para>
    ///
    /// <para><b>Authority.</b> The server offers (<see cref="ServerOffer"/>), resolves
    /// (<see cref="ServerResolve"/> / <see cref="ServerAutoResolve"/>), and applies the
    /// chosen option. The owning client only sends intent via
    /// <see cref="RequestPickRpc"/> and <see cref="RequestBuyRerollRpc"/>.</para>
    ///
    /// <para><b>Generation</b> lives in <see cref="DraftService"/>; this component is just
    /// the replicated state + the pick/buy entry points.</para>
    /// </remarks>
    public class PlayerDraft : NetworkBehaviour
    {
        // ----- Static registry (mirrors PlayerGoldManager / PlayerTowerDeck) -----

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

        public static PlayerDraft GetForClient(ulong clientId)
        {
            s_byClientId.TryGetValue(clientId, out var draft);
            return draft;
        }

        public static PlayerDraft Local
        {
            get
            {
                var nm = NetworkManager.Singleton;
                if (nm == null || !nm.IsClient) return null;
                return GetForClient(nm.LocalClientId);
            }
        }

        // ----- Networked state --------------------------------------------

        // The DraftOptionIds currently offered to this player. Empty = no active draft.
        private NetworkList<int> currentOptionIds;

        /// <summary>Fired on every peer when the offered set changes (offered, picked,
        /// cleared). The HUD subscribes to show/hide/rebuild the draft overlay.</summary>
        public event System.Action OnDraftChanged;

        private void Awake()
        {
            currentOptionIds = new NetworkList<int>();
        }

        public override void OnNetworkSpawn()
        {
            s_byClientId[OwnerClientId] = this;
            currentOptionIds.OnListChanged += HandleListChanged;
        }

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

        private void HandleListChanged(NetworkListEvent<int> _) => OnDraftChanged?.Invoke();

        // ----- Read API ---------------------------------------------------

        /// <summary>True if the player currently has options to choose from.</summary>
        public bool HasActiveDraft => currentOptionIds.Count > 0;

        public int OptionCount => currentOptionIds.Count;

        public int GetOptionId(int index) => currentOptionIds[index];

        private bool IsOffered(int optionId)
        {
            for (int i = 0; i < currentOptionIds.Count; i++)
                if (currentOptionIds[i] == optionId) return true;
            return false;
        }

        // ----- Server: offer / resolve ------------------------------------

        /// <summary>Server-only: replace the offered set with <paramref name="ids"/>.</summary>
        public void ServerOffer(IReadOnlyList<int> ids)
        {
            if (!IsServer) return;
            currentOptionIds.Clear();
            if (ids == null) return;
            for (int i = 0; i < ids.Count; i++)
                currentOptionIds.Add(ids[i]);
        }

        /// <summary>
        /// Server-only: apply one of the currently-offered options by id, then clear the
        /// draft. No-op (returns false) if the id wasn't offered or the option fails to apply.
        /// </summary>
        private bool ServerResolve(int optionId)
        {
            if (!IsServer) return false;
            if (!IsOffered(optionId)) return false;

            var option = DraftPool.Instance != null ? DraftPool.Instance.Get(optionId) : null;
            bool applied = option != null && option.ServerApply(OwnerClientId);
            if (!applied)
                Debug.LogWarning($"[PlayerDraft] Option {optionId} could not be applied for " +
                                 $"client {OwnerClientId}.");

            currentOptionIds.Clear();
            return applied;
        }

        /// <summary>
        /// Server-only: if the player still has an unpicked draft, auto-resolve it by
        /// applying the first offered option. Called at prep end so a free reward is never
        /// wasted by an idle player.
        /// </summary>
        public void ServerAutoResolve()
        {
            if (!IsServer) return;
            if (currentOptionIds.Count == 0) return;
            ServerResolve(currentOptionIds[0]);
        }

        // ----- Owner → server RPCs ----------------------------------------

        /// <summary>Owning client: pick one of the offered options by id.</summary>
        [Rpc(SendTo.Server, RequireOwnership = true)]
        public void RequestPickRpc(int optionId)
        {
            ServerResolve(optionId);
        }

        /// <summary>
        /// Owning client: buy a fresh roll of options for gold. Rejected if the player can't
        /// afford it or already has an unresolved draft (resolve the current one first so a
        /// free pick is never silently overwritten).
        /// </summary>
        [Rpc(SendTo.Server, RequireOwnership = true)]
        public void RequestBuyRerollRpc()
        {
            if (HasActiveDraft) return; // resolve the pending draft before buying another

            var service = DraftService.Instance;
            if (service == null) return;

            var gold = PlayerGoldManager.GetForClient(OwnerClientId);
            int cost = service.RerollCost;
            if (gold == null || gold.CurrentGold < cost) return;

            gold.DeductGold(cost);
            service.ServerOfferTo(OwnerClientId);
        }
    }
}
