Code/TailBox/Domain/Utilities/TailBoxUtilityCompiler.cs
using System.Linq;
using System.Text;

namespace Sandbox.TailBox;

internal static class TailBoxUtilityCompiler
{
	public static bool TryCompile( string className, TailBoxTheme theme, out TailBoxUtilityRule rule, out string warning )
	{
		var generated = TryCompileDetailed( className, theme, out rule, out var skipped );
		warning = skipped?.Detail;
		return generated;
	}

	public static bool TryCompileDetailed( string className, TailBoxTheme theme, out TailBoxUtilityRule rule, out TailBoxCompileDiagnostic skipped )
	{
		rule = null;
		skipped = null;
		theme ??= TailBoxTheme.CreateEmpty();

		if ( !TailBoxCandidateParser.TryParse( className, out var candidate, out skipped ) )
			return false;

		foreach ( var variant in candidate.Variants )
		{
			if ( variant.Kind == TailBoxVariantKind.Pseudo )
				continue;

			skipped = new TailBoxCompileDiagnostic
			{
				ClassName = className,
				Reason = variant.Kind switch
				{
					TailBoxVariantKind.Selector => TailBoxSkipReason.UnsupportedSelectorVariant,
					_ => TailBoxSkipReason.UnsupportedVariant
				},
				Detail = variant.Detail
			};
			return false;
		}

		if ( !TailBoxUtilityRegistry.TryCompile( candidate, theme, out var declarations, out skipped ) )
			return false;

		var unsupportedDeclaration = declarations.FirstOrDefault( declaration => !TailBoxStyleCapabilities.AllowsDeclaration( declaration ) );
		if ( unsupportedDeclaration != default )
		{
			skipped = new TailBoxCompileDiagnostic
			{
				ClassName = className,
				Reason = TailBoxStyleCapabilities.IsSupportedProperty( unsupportedDeclaration.Property )
					? TailBoxSkipReason.UnsupportedValue
					: TailBoxSkipReason.UnsupportedProperty,
				Detail = TailBoxStyleCapabilities.IsSupportedProperty( unsupportedDeclaration.Property )
					? $"s&box does not support value '{unsupportedDeclaration.Value}' for property '{unsupportedDeclaration.Property}'."
					: $"s&box does not support property '{unsupportedDeclaration.Property}'."
			};
			return false;
		}

		var pseudoSelector = new StringBuilder();
		foreach ( var variant in candidate.Variants )
		{
			pseudoSelector.Append( variant.SelectorSuffix );
		}

		rule = new TailBoxUtilityRule
		{
			ClassName = className,
			Selector = "." + EscapeClassSelector( className ) + pseudoSelector
		};
		rule.Declarations.AddRange( declarations );
		return true;
	}

	public static string EscapeClassSelector( string className )
	{
		return TailBoxText.EscapeIdentifierForSboxSelector( className );
	}
}