Editor/Projection/Tests/CSharpOpExhaustivenessTest.cs
using System.Linq;
using System.Reflection;
using Grains.RazorDesigner.Projection.CSharp;

namespace Grains.RazorDesigner.Projection.Tests;

public static class CSharpOpExhaustivenessTest
{
    public static (bool pass, string message) Run()
    {
        var ops = new CSharpOp[]
        {
            // File-level scaffold (5)
            new HeaderBanner( "Foo", "Bar.Baz" ),
            new UsingDirective( "Sandbox.UI" ),
            new NamespaceOpen( "My.Namespace" ),
            new ClassOpen( "MyPanel", "PanelComponent" ),
            new ClassClose(),

            // Member-level (3)
            new FieldDecl( "private", "int", "_count", "0", false ),
            new MethodOpen( "public", true, false, "void", "OnAfterTreeRender", "bool firstRender" ),
            new MethodClose(),

            // Body-level (5)
            new Statement( "x = 1" ),
            new BlockOpen( "if ( x > 0 )" ),
            new BlockClose(),
            new BlankLine(),
            new Comment( "Auto-generated — do not edit." ),
        };

        int arrayCount = ops.Length;

        var reflectedTypes = typeof( CSharpOp ).Assembly
            .GetTypes()
            .Where( t => t != typeof( CSharpOp ) && typeof( CSharpOp ).IsAssignableFrom( t ) && !t.IsAbstract )
            .ToArray();

        int reflectedCount = reflectedTypes.Length;

        if ( arrayCount != reflectedCount )
        {
            var reflectedNames = string.Join( ", ", reflectedTypes.Select( t => t.Name ) );
            return (false,
                $"CSharpOpExhaustivenessTest MISMATCH: inline array has {arrayCount} ops " +
                $"but reflection found {reflectedCount} CSharpOp-derived types: {reflectedNames}. " +
                "Add the missing leaf to the inline array AND to CSharpApplier's switch in the same commit.");
        }

        foreach ( var op in ops )
        {
            try
            {
                CSharpApplier.ApplyOpToScratch( op );
            }
            catch ( System.Diagnostics.UnreachableException ex )
            {
                return (false,
                    $"CSharpOpExhaustivenessTest: CSharpApplier.ApplyOpToScratch threw for " +
                    $"'{op.GetType().Name}': {ex.Message}");
            }
        }

        return (true,
            $"13/13 known leaves accepted by CSharpApplier.ApplyOpToScratch.");
    }
}