Editor/Projection/CSharp/Projectors/ActionEmitter.cs
using System.Collections.Generic;
using System.Linq;
using Grains.RazorDesigner.Wiring;

namespace Grains.RazorDesigner.Projection.CSharp.Projectors;

public static class ActionEmitter
{
    public static void Emit( Grains.RazorDesigner.Wiring.Action action, List<CSharpOp> ops, CSharpProjectorContext ctx )
    {
        switch ( action )
        {
            case SetAction set:
                ops.Add( new Statement(
                    EmitTarget( set.Target, ctx ) + " = " + ExpressionEmitter.Emit( set.Value, ctx ) ) );
                break;

            case CallAction call:
                var prefix = call.Await ? "await " : "";
                if ( call.Await ) ctx.RecordAwait();
                var callee = ExpressionEmitter.EmitCallee( call.Callee, ctx );
                var args   = string.Join( ", ", call.Args.Select( a => ExpressionEmitter.Emit( a, ctx ) ) );
                ops.Add( new Statement( prefix + callee + "(" + args + ")" ) );
                break;

            case IfAction iff:
                ops.Add( new BlockOpen( "if ( " + ExpressionEmitter.Emit( iff.Condition, ctx ) + " )" ) );
                foreach ( var inner in iff.Then ) Emit( inner, ops, ctx );
                ops.Add( new BlockClose() );
                if ( iff.Else != null && iff.Else.Count > 0 )
                {
                    ops.Add( new BlockOpen( "else" ) );
                    foreach ( var inner in iff.Else ) Emit( inner, ops, ctx );
                    ops.Add( new BlockClose() );
                }
                break;

            case StateHasChangedAction:
                ops.Add( new Statement( "StateHasChanged()" ) );
                break;

            case LogAction log:
                var method = log.Level switch
                {
                    LogLevel.Info    => "Log.Info",
                    LogLevel.Warning => "Log.Warning",
                    LogLevel.Error   => "Log.Error",
                    _                => "Log.Info",
                };
                ops.Add( new Statement( method + "( " + ExpressionEmitter.Emit( log.Message, ctx ) + " )" ) );
                break;

            case ReturnAction ret:
                if ( ret.Value is null )
                    ops.Add( new Statement( "return" ) );
                else
                    ops.Add( new Statement( "return " + ExpressionEmitter.Emit( ret.Value, ctx ) ) );
                break;

            case InlineAction inl:
                foreach ( var rawLine in (inl.Code ?? "").Split( '\n' ) )
                {
                    var afterCarriageReturn = rawLine.TrimEnd( '\r' );
                    var withoutSemicolon = afterCarriageReturn.EndsWith( ";" )
                        ? afterCarriageReturn[..^1]
                        : afterCarriageReturn;
                    var cleaned = withoutSemicolon.TrimEnd();
                    if ( string.IsNullOrWhiteSpace( cleaned ) ) continue;
                    ops.Add( new Statement( cleaned ) );
                }
                break;

            default:
                throw new System.Diagnostics.UnreachableException(
                    $"ActionEmitter: unhandled Action variant '{action?.GetType().Name}'. " +
                    "Add an arm here AND add the leaf to Action.cs's JsonDerivedType list in the same commit." );
        }
    }


    private static string EmitTarget( TargetRef target, CSharpProjectorContext ctx )
    {
        switch ( target )
        {
            case SymbolTarget sym:
                if ( ctx.Symbols.TryGetValue( sym.SymbolId, out var symbol ) )
                    return symbol.Name;
                return $"/* missing symbol {sym.SymbolId} */";

            case null:
                return "/* null target */";

            default:
                throw new System.Diagnostics.UnreachableException(
                    $"ActionEmitter.EmitTarget: unhandled TargetRef variant '{target.GetType().Name}'. " +
                    "Add an arm here AND add the leaf to TargetRef.cs's JsonDerivedType list in the same commit." );
        }
    }
}