Editor/FenceLibrary/FenceBlockerMenu.cs
using System.Linq;

namespace Editor;

internal static class FenceBlockerMenu
{
	[Menu( "Editor", "Fence Builder/Add Or Update Blockers On Selection", "crop_square" )]
	public static void AddOrUpdateBlockersOnSelection()
	{
		var session = SceneEditorSession.Active;
		if ( session is null )
		{
			EditorUtility.DisplayDialog( "No Active Scene", "Open a scene or prefab session first." );
			return;
		}

		var selectedObjects = session.Selection.OfType<GameObject>().Where( x => x.IsValid() ).ToArray();
		if ( selectedObjects.Length == 0 )
		{
			EditorUtility.DisplayDialog( "No Fence Selection", "Select one or more placed fence roots or fence segment objects first." );
			return;
		}

		var existingBlockerObjects = selectedObjects
			.SelectMany( EnumerateHierarchy )
			.Where( x => x.IsValid() && string.Equals( x.Name, "NavBlocker", System.StringComparison.Ordinal ) )
			.ToArray();
		var existingBlockerAreas = selectedObjects
			.SelectMany( EnumerateHierarchy )
			.Select( x => x.Components.Get<NavMeshArea>( FindMode.EverythingInSelf ) )
			.Where( x => x is not null )
			.ToArray();

		using var sessionScope = SceneEditorSession.Scope();
		using var sceneScope = session.Scene.Push();
		using var undoScope = session.UndoScope( "Add Fence Blockers" )
			.WithGameObjectChanges( selectedObjects, GameObjectUndoFlags.All )
			.WithComponentChanges( existingBlockerAreas )
			.WithGameObjectDestructions( existingBlockerObjects )
			.WithComponentCreations()
			.Push();

		var blockerCount = 0;
		foreach ( var selectedObject in selectedObjects )
		{
			blockerCount += FenceDefinitionEditorUtility.AddOrUpdateBlockerVolumes( selectedObject );
		}

		EditorUtility.DisplayDialog( "Fence Blockers Updated", $"Added or updated {blockerCount} fence blocker volume(s)." );
	}

	private static System.Collections.Generic.IEnumerable<GameObject> EnumerateHierarchy( GameObject root )
	{
		if ( !root.IsValid() )
			yield break;

		yield return root;

		foreach ( var child in root.Children )
		{
			foreach ( var descendant in EnumerateHierarchy( child ) )
			{
				yield return descendant;
			}
		}
	}
}