UnitTests/TailBoxConfigTests.cs
using Sandbox.TailBox;
using System;
using System.Linq;

[TestClass]
public sealed class TailBoxConfigTests
{
	[TestMethod]
	public void LoadJsonMergesDefaultsAndIgnoresEmptyOverrides()
	{
		var loaded = TailBoxConfig.LoadJson(
			"""
			{
				"outputPath": "",
				"content": [],
				"safelist": null,
				"spacing": {
					"card": "22px",
					"": "ignored",
					"empty": ""
				},
				"colors": {
					"accent": "#111111",
					"brand": "#234567"
				}
			}
			""",
			"C:/Project/tailwand.config.json" );

		Assert.AreEqual( "Code/tailwand.generated.scss", loaded.OutputPath );
		CollectionAssert.AreEqual( new[] { "Code/**/*.razor" }, loaded.Content.ToArray() );
		Assert.AreEqual( 0, loaded.Safelist.Count );
		Assert.AreEqual( "22px", loaded.Spacing["CARD"] );
		Assert.IsFalse( loaded.Spacing.ContainsKey( "empty" ) );
		Assert.AreEqual( "#111111", loaded.Colors["ACCENT"] );
		Assert.AreEqual( "#234567", loaded.Colors["brand"] );
		Assert.AreEqual( "C:/Project/tailwand.config.json", loaded.ConfigPath );
	}

	[TestMethod]
	public void ToJsonUsesCamelCaseAndDoesNotPersistConfigPath()
	{
		var config = TailBoxConfig.CreateDefault();
		config.OutputPath = "Assets/Generated/tailbox.scss";
		config.ConfigPath = "C:/Project/tailwand.config.json";
		config.Safelist.Add( "flex" );

		var json = config.ToJson();
		var roundTrip = TailBoxConfig.LoadJson( json, "C:/Project/loaded.config.json" );

		StringAssert.Contains( json, "\"outputPath\"" );
		StringAssert.Contains( json, "\"fontSizes\"" );
		Assert.IsFalse( json.Contains( "ConfigPath", StringComparison.Ordinal ) );
		Assert.AreEqual( "Assets/Generated/tailbox.scss", roundTrip.OutputPath );
		Assert.AreEqual( "flex", roundTrip.Safelist.Single() );
		Assert.AreEqual( "C:/Project/loaded.config.json", roundTrip.ConfigPath );
	}

	[TestMethod]
	public void GetOutputFullPathNormalizesRelativeAndRootedPaths()
	{
		var config = TailBoxConfig.CreateDefault();
		config.OutputPath = "Code\\Generated\\tailbox.scss";

		Assert.AreEqual(
			"C:/game/project/Code/Generated/tailbox.scss",
			config.GetOutputFullPath( "C:\\game\\project" ) );

		config.OutputPath = "D:\\exports\\tailbox.scss";
		Assert.AreEqual( "D:/exports/tailbox.scss", config.GetOutputFullPath( "C:/game/project" ) );
	}

	[TestMethod]
	public void InvalidJsonReportsTheConfigPath()
	{
		var exception = Assert.ThrowsException<InvalidOperationException>(
			() => TailBoxConfig.LoadJson( "{", "C:/Project/tailwand.config.json" ) );

		StringAssert.Contains( exception.Message, "C:/Project/tailwand.config.json" );
		Assert.IsNotNull( exception.InnerException );
	}

	[TestMethod]
	public void RuntimeConfigFileMethodsStayEditorOnly()
	{
		Assert.ThrowsException<NotSupportedException>( () => TailBoxConfig.Exists( "C:/Project" ) );
		Assert.ThrowsException<NotSupportedException>( () => TailBoxConfig.Load( "C:/Project" ) );
		Assert.ThrowsException<NotSupportedException>( () => TailBoxConfig.LoadFile( "C:/Project/tailwand.config.json" ) );
		Assert.ThrowsException<NotSupportedException>( () => TailBoxConfig.SaveDefault( "C:/Project" ) );
		Assert.ThrowsException<NotSupportedException>( () => TailBoxConfig.CreateDefault().Save( "C:/Project" ) );
	}

	[TestMethod]
	public void ThemeFactoryAcceptsNullConfigAndCopiesDictionaries()
	{
		var defaults = TailBoxThemeFactory.FromConfig( null );
		Assert.AreEqual( "#d7b46a", defaults.Colors["accent"] );

		var config = TailBoxConfig.CreateDefault();
		config.Spacing["card"] = "22px";
		config.Colors = null;

		var theme = TailBoxThemeFactory.FromConfig( config );
		config.Spacing["card"] = "99px";

		Assert.AreEqual( "22px", theme.Spacing["card"] );
		Assert.AreEqual( 0, theme.Colors.Count );
	}
}