using UnityEngine;
using TD.Core;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace TD.Levels
{
    /// <summary>
    /// Authoring volume defining the playable map area — the bounds the builder can move within
    /// and the camera can pan to. Has no gameplay payload (no owner, no placement validity, no
    /// spawner data, no leak target). Multiple instances are allowed; their union defines the
    /// "map area" as referenced in the per-tile data layering spec.
    /// </summary>
    /// <remarks>
    /// Map area = where the player's attention can go.
    /// Gameplay area = union of PlayerZone, Spawner, LeakExit, Goal volumes (where rules apply).
    /// Buffer area = map area minus gameplay area (visible terrain, no gameplay, no building).
    ///
    /// Coverage rule: every gameplay volume's tiles must be a subset of the map area. The bake
    /// validates this in Phase 5 (P5-12) and fails with a hard error if any gameplay volume
    /// pokes outside the map area.
    ///
    /// Drawn as a thicker outline only (no fill) so it reads as a boundary marker rather than
    /// as a translucent overlay on top of the gameplay volumes. The "always show" toggle on
    /// LevelAuthoring defaults to true for this volume type because the map boundary is
    /// generally useful to see during all authoring, not just when this volume is selected.
    /// </remarks>
    public class MapAreaVolume : VolumeBase
    {
        // Map area draws BELOW player zones (which sit at 0.05). Just above the LevelAuthoring
        // map-bounds line (0.01) so it doesn't z-fight with that wireframe.
        private const float FillYLevel = 0.02f;

        protected override bool GetAlwaysShowToggle(LevelAuthoring authoring)
        {
            return authoring.alwaysShowMapArea;
        }

        private void OnDrawGizmosSelected()
        {
            DrawGizmosCore();
        }

        private void OnDrawGizmos()
        {
            if (ShouldDrawAlwaysOn())
            {
                DrawGizmosCore();
            }
        }

        private void DrawGizmosCore()
        {
            Color baseColor = PlayerColors.MapArea;
            DrawThickRectangularOutline(baseColor, yLevel: FillYLevel);

#if UNITY_EDITOR
            var col = Collider;
            if (col != null)
            {
                Vector3 labelPos = new Vector3(col.bounds.center.x, FillYLevel + 0.1f, col.bounds.center.z);
                Handles.Label(labelPos, "Map Area");
            }
#endif
        }

        // Draws the volume's tight tile rectangle as a thicker outline. Unity's Gizmos.DrawLine
        // has no width parameter, so we approximate thickness by drawing the rectangle three
        // times with small lateral offsets. The offsets are in tile units; 0.04 reads as a
        // visibly thicker line at typical scene-view zoom without looking like multiple lines.
        private void DrawThickRectangularOutline(Color outlineColor, float yLevel)
        {
            if (!TryGetTightTileRect(out Vector2Int minTile, out Vector2Int maxTile)) return;

            const float thickness = 0.04f;
            float tileSize = GridCoordinates.TILE_SIZE;

            // Three concentric rectangles: the original edge, slightly inset, slightly outset.
            DrawOutlineAtInset(minTile, maxTile, tileSize, yLevel, 0f, outlineColor);
            DrawOutlineAtInset(minTile, maxTile, tileSize, yLevel, +thickness, outlineColor);
            DrawOutlineAtInset(minTile, maxTile, tileSize, yLevel, -thickness, outlineColor);
        }

        // Tile (x, y) spans world XZ [x, x+1] (edge-aligned). The outline corners are at the
        // tile rect's outer edges; `inset` shifts them outward (positive) or inward (negative)
        // to draw the concentric thickness rectangles.
        private static void DrawOutlineAtInset(Vector2Int minTile, Vector2Int maxTile, float tileSize,
                                               float yLevel, float inset, Color color)
        {
            float minX = minTile.x * tileSize - inset;
            float maxX = (maxTile.x + 1) * tileSize + inset;
            float minZ = minTile.y * tileSize - inset;
            float maxZ = (maxTile.y + 1) * tileSize + inset;
            Vector3 sw = new Vector3(minX, yLevel, minZ);
            Vector3 se = new Vector3(maxX, yLevel, minZ);
            Vector3 ne = new Vector3(maxX, yLevel, maxZ);
            Vector3 nw = new Vector3(minX, yLevel, maxZ);

            Color prev = Gizmos.color;
            Gizmos.color = color;
            Gizmos.DrawLine(sw, se);
            Gizmos.DrawLine(se, ne);
            Gizmos.DrawLine(ne, nw);
            Gizmos.DrawLine(nw, sw);
            Gizmos.color = prev;
        }
    }
}