Editor/Widgets/SuiCompileResultsWidget.cs
using System.Collections.Generic;
using System.Text;
using Editor;
using Sandbox;
using SboxUiDesigner.Generation;

namespace SboxUiDesigner.EditorUi.Widgets;

/// <summary>
/// Compile Results dock — surfaces the output of the most recent compile pass.
///
/// Two render paths:
/// - <see cref="DisplayResult"/> — generation only (no disk write). Used by
///   M9-era compile preview.
/// - <see cref="DisplayCompileResult"/> — generation + writer report
///   (M12, full disk pipeline). Renders categorised sections.
/// </summary>
public class SuiCompileResultsWidget : Widget
{
	private Label _summary;
	private TextEdit _detail;

	public SuiCompileResultsWidget( Widget parent = null ) : base( parent )
	{
		WindowTitle = "Compile Results";
		Name = "SuiCompileResults";

		Layout = Layout.Column();
		Layout.Margin = 8;
		Layout.Spacing = 6;

		_summary = new Label( "(compile not run yet)", this );
		_summary.WordWrap = true;
		_summary.SetStyles( "color: #9ca3af; font-size: 11px;" );
		Layout.Add( _summary );

		_detail = new TextEdit( this );
		_detail.PlainText = "// Run Compile to see the full report here.";
		_detail.ReadOnly = true;
		Layout.Add( _detail, 1 );
	}

	/// <summary>
	/// Generation-only display (no disk write). Used when the user clicks
	/// Compile but no output folder is configured.
	/// </summary>
	public void DisplayResult( SuiGenerationResult result )
	{
		if ( result == null )
		{
			_summary.Text = "(no result)";
			_detail.PlainText = "";
			return;
		}

		var sb = new StringBuilder();
		sb.Append( result.Ok ? "✓ Compile OK — " : "✗ Compile failed — " );
		sb.Append( result.Files.Count ).Append( " file" ).Append( result.Files.Count == 1 ? "" : "s" );
		if ( result.Errors.Count > 0 ) sb.Append( ", " ).Append( result.Errors.Count ).Append( " error" ).Append( result.Errors.Count == 1 ? "" : "s" );
		if ( result.Warnings.Count > 0 ) sb.Append( ", " ).Append( result.Warnings.Count ).Append( " warning" ).Append( result.Warnings.Count == 1 ? "" : "s" );
		_summary.Text = sb.ToString();

		var detail = new StringBuilder();
		AppendList( detail, "Generated (in-memory only)", result.Files, f => f.Path );
		AppendList( detail, "Errors", result.Errors, s => s );
		AppendList( detail, "Warnings", result.Warnings, s => s );
		if ( result.Files.Count > 0 )
		{
			detail.AppendLine();
			detail.AppendLine( "── First file content ──" );
			detail.AppendLine( result.Files[0].Content ?? "" );
		}
		_detail.PlainText = detail.ToString();
	}

	/// <summary>
	/// Full M12 compile result: includes write classification (Generated /
	/// Skipped / Preserved / Conflicts / Obsolete) and backup folder.
	/// </summary>
	public void DisplayCompileResult( SuiGenerationResult generation, SuiCompileResult compile )
	{
		if ( compile == null ) { DisplayResult( generation ); return; }

		var headline = compile.Ok ? "✓ Compile OK" : "✗ Compile failed";
		var sb = new StringBuilder();
		sb.Append( headline );
		sb.Append( $" — Generated {compile.Generated.Count}, Skipped {compile.Skipped.Count}, Preserved {compile.Preserved.Count}" );
		if ( compile.UserOwned.Count > 0 ) sb.Append( $", User {compile.UserOwned.Count}" );
		if ( compile.Conflicts.Count > 0 ) sb.Append( $", ⚠ Conflicts {compile.Conflicts.Count}" );
		if ( compile.Obsolete.Count > 0 ) sb.Append( $", Obsolete {compile.Obsolete.Count}" );
		if ( compile.Errors.Count > 0 ) sb.Append( $", ✗ Errors {compile.Errors.Count}" );
		if ( compile.Warnings.Count > 0 ) sb.Append( $", Warnings {compile.Warnings.Count}" );
		_summary.Text = sb.ToString();

		var d = new StringBuilder();
		d.AppendLine( $"Output folder: {compile.OutputFolder ?? "(unset)"}" );
		if ( compile.BackupFolder != null )
			d.AppendLine( $"Backup folder: {compile.BackupFolder}" );
		d.AppendLine();

		AppendCompileSection( d, "Generated", compile.Generated );
		AppendCompileSection( d, "Preserved (overwritten — prior content backed up)", compile.Preserved, includeBackup: true );
		AppendCompileSection( d, "User-Owned (.User.scss — created if missing, never overwritten)", compile.UserOwned );
		AppendCompileSection( d, "Skipped (unchanged)", compile.Skipped );
		AppendCompileSection( d, "⚠ Conflicts (NOT touched — resolve manually)", compile.Conflicts, includeReason: true );
		AppendCompileSection( d, "Obsolete (in old manifest, not in this generation — review)", compile.Obsolete );

		AppendList( d, "Errors", compile.Errors, s => s );
		AppendList( d, "Warnings", compile.Warnings, s => s );

		_detail.PlainText = d.ToString();
	}

	// ─────────────────────────────────────────────────────────────────────
	//  helpers
	// ─────────────────────────────────────────────────────────────────────

	private static void AppendCompileSection(
		StringBuilder sb, string title,
		List<SuiCompileFileEntry> entries,
		bool includeBackup = false, bool includeReason = false )
	{
		if ( entries == null || entries.Count == 0 ) return;
		sb.Append( "── " ).Append( title ).Append( " (" ).Append( entries.Count ).AppendLine( ") ──" );
		foreach ( var e in entries )
		{
			sb.Append( "  • " ).Append( e.RelativePath );
			if ( !string.IsNullOrEmpty( e.Sha256 ) )
				sb.Append( "  [" ).Append( e.Sha256.Substring( 0, System.Math.Min( 12, e.Sha256.Length ) ) ).Append( "…]" );
			sb.AppendLine();
			if ( includeBackup && !string.IsNullOrEmpty( e.BackupPath ) )
				sb.Append( "      backup: " ).AppendLine( e.BackupPath );
			if ( includeReason && !string.IsNullOrEmpty( e.ConflictReason ) )
				sb.Append( "      reason: " ).AppendLine( e.ConflictReason );
		}
		sb.AppendLine();
	}

	private static void AppendList<T>(
		StringBuilder sb, string title,
		List<T> items, System.Func<T, string> selector )
	{
		if ( items == null || items.Count == 0 ) return;
		sb.Append( "── " ).Append( title ).Append( " (" ).Append( items.Count ).AppendLine( ") ──" );
		foreach ( var i in items )
			sb.Append( "  • " ).AppendLine( selector( i ) );
		sb.AppendLine();
	}
}