// Assets/_Project/Scripts/Combat/TowerRangeIndicator.cs
using UnityEngine;
using UnityEngine.Rendering.Universal;
using TD.Gameplay;

namespace TD.Combat
{
    /// <summary>
    /// Displays a translucent decal circle representing this tower's attack range
    /// when the local player selects the tower.
    /// </summary>
    /// <remarks>
    /// <b>Visual only.</b> No networking — selection is a local UI concept.
    /// All clients independently show the range indicator for whatever tower
    /// they have selected.
    ///
    /// <b>Prefab setup:</b>
    /// <list type="number">
    ///   <item>Add this component to the tower prefab root (alongside TowerInstance).</item>
    ///   <item>Add a child GameObject named "RangeIndicator".</item>
    ///   <item>Add a <c>DecalProjector</c> to that child and assign it to
    ///         <see cref="rangeProjector"/> (or leave it unassigned — auto-found
    ///         via <c>GetComponentInChildren</c> in Start).</item>
    ///   <item>Assign a translucent range-circle material to the DecalProjector.</item>
    /// </list>
    ///
    /// <b>Sizing:</b> The projector diameter is set once in <c>Start</c> from
    /// <c>TowerDefinition.Range</c>. Towers are static, so no per-frame resize is needed.
    ///
    /// <b>Subscription timing:</b> Follows the same deferred-subscribe pattern as
    /// <see cref="SelectionVisualizer"/> — retries until <see cref="SelectionState"/>
    /// is available, then stops polling.
    /// </remarks>
    public class TowerRangeIndicator : MonoBehaviour
    {
        [Tooltip("DecalProjector used to render the range circle on the ground. " +
                 "Auto-found via GetComponentInChildren if left empty.")]
        [SerializeField] private DecalProjector rangeProjector;

        [Tooltip("Vertical extent of the decal projection volume in world units. " +
                 "Must be tall enough to project onto terrain at any height in your map.")]
        [SerializeField] private float projectionDepth = 50f;

        private TowerInstance towerInstance;
        private bool          subscribed;

        // ----- Lifecycle ---------------------------------------------------

        private void Start()
        {
            towerInstance = GetComponent<TowerInstance>();
            if (towerInstance == null)
            {
                Debug.LogError("[TowerRangeIndicator] No TowerInstance found on this " +
                               "GameObject. TowerRangeIndicator must sit on the same " +
                               "prefab root as TowerInstance.");
                enabled = false;
                return;
            }

            if (rangeProjector == null)
                rangeProjector = GetComponentInChildren<DecalProjector>();
            if (rangeProjector == null)
            {
                Debug.LogError("[TowerRangeIndicator] No DecalProjector found. " +
                               "Add one as a child of this GameObject and assign it " +
                               "to the rangeProjector field.");
                enabled = false;
                return;
            }

            // TowerInstance resolves its Definition in OnNetworkSpawn, which runs
            // before Start, so Definition is guaranteed available here.
            float range    = towerInstance.Definition != null ? towerInstance.Definition.Range : 0f;
            float diameter = range * 2f;

            rangeProjector.size  = new Vector3(diameter, diameter, projectionDepth);
            rangeProjector.pivot = Vector3.zero;

            // DecalProjector projects along its local +Z axis; rotate 90° around X
            // to project downward onto the ground plane.
            rangeProjector.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
            rangeProjector.transform.localPosition = Vector3.zero;

            rangeProjector.enabled = false;

            TrySubscribe();
        }

        private void OnDestroy()
        {
            Unsubscribe();
        }

        private void Update()
        {
            // Retry subscription each frame until SelectionState is ready.
            // Once subscribed the branch short-circuits immediately.
            if (!subscribed) TrySubscribe();
        }

        // ----- Subscription -----------------------------------------------

        private void TrySubscribe()
        {
            if (subscribed) return;
            var sel = SelectionState.Instance;
            if (sel == null) return;

            sel.OnSelectionChanged += HandleSelectionChanged;
            subscribed = true;

            // Catch whatever is already selected before we subscribed.
            HandleSelectionChanged(sel.SelectedObject);
        }

        private void Unsubscribe()
        {
            if (!subscribed) return;
            var sel = SelectionState.Instance;
            if (sel != null) sel.OnSelectionChanged -= HandleSelectionChanged;
            subscribed = false;
        }

        // ----- Selection handler ------------------------------------------

        private void HandleSelectionChanged(ISelectable newSelection)
        {
            if (rangeProjector == null) return;

            // Show only when THIS tower's TowerInstance is the selected object.
            rangeProjector.enabled = (object)newSelection == (object)towerInstance;
        }
    }
}
