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

[TestClass]
public sealed class TailBoxPipelineTests
{
	[TestMethod]
	public void GenerateFromSourcesHandlesNullSourcesAndConfig()
	{
		var result = new TailBoxGenerator().GenerateFromSources( null, null, "C:/Project", "C:/Project/Code/out.scss" );

		Assert.AreEqual( "C:/Project", result.ProjectRoot );
		Assert.AreEqual( "C:/Project/Code/out.scss", result.OutputPath );
		Assert.AreEqual( 0, result.ScannedFileCount );
		Assert.AreEqual( 0, result.DiscoveredClassCount );
		Assert.AreEqual( 0, result.GeneratedClassCount );
		Assert.AreEqual( 0, result.SkippedClassCount );
		Assert.IsFalse( result.WroteFile );
		StringAssert.Contains( result.GeneratedScss, "Source files scanned: 0" );
		StringAssert.Contains( result.GeneratedScss, "Config: tailwand.config.json" );
	}

	[TestMethod]
	public void GenerateFromSourcesDeduplicatesSortsAndKeepsFirstSourceForSkips()
	{
		var firstSource = "C:/Project/Code/First.razor";
		var secondSource = "C:/Project/Code/Second.razor";
		var sources = new[]
		{
			new TailBoxSourceText( firstSource, "<div class=\"p-4 grid flex\"></div>" ),
			new TailBoxSourceText( secondSource, "<div class=\"grid text-accent\"></div>" )
		};
		var config = TailBoxConfig.CreateDefault();
		config.Safelist.Add( "text-accent; flex p-4" );

		var result = new TailBoxGenerator().GenerateFromSources( sources, config, "C:/Project", "C:/Project/Code/out.scss" );

		CollectionAssert.AreEqual( new[] { "flex", "p-4", "text-accent" }, result.GeneratedClasses.ToArray() );
		CollectionAssert.AreEqual( new[] { "grid" }, result.SkippedClasses.ToArray() );
		Assert.AreEqual( 4, result.DiscoveredClassCount );
		Assert.AreEqual( firstSource, result.Skipped.Single().SourcePath );
		Assert.IsTrue( result.Warnings.Single().StartsWith( "grid:", StringComparison.Ordinal ) );
	}

	[TestMethod]
	public void GenerateFromSourcesRendersConfigFileNameAndScannedFileCount()
	{
		var config = TailBoxConfig.CreateDefault();
		config.ConfigPath = "C:/Project/Config/tailwand.custom.json";
		var sources = new[]
		{
			new TailBoxSourceText( "C:/Project/Code/Screen.razor", "<div class=\"flex\"></div>" ),
			new TailBoxSourceText( "C:/Project/Code/Empty.razor", "" )
		};

		var result = new TailBoxGenerator().GenerateFromSources( sources, config, "C:/Project" );

		Assert.AreEqual( 2, result.ScannedFileCount );
		StringAssert.Contains( result.GeneratedScss, "Source files scanned: 2" );
		StringAssert.Contains( result.GeneratedScss, "Config: tailwand.custom.json" );
		StringAssert.Contains( result.GeneratedScss, ".flex {" );
	}

	[TestMethod]
	public void ScanClassesFromSourcesSortsAndDeduplicates()
	{
		var sources = new[]
		{
			new TailBoxSourceText( "A.razor", "<div class=\"p-4 flex\"></div>" ),
			new TailBoxSourceText( "B.razor", "<div class=\"flex text-accent\"></div>" )
		};

		var classes = new TailBoxGenerator().ScanClassesFromSources( sources );

		CollectionAssert.AreEqual( new[] { "flex", "p-4", "text-accent" }, classes.ToArray() );
	}

	[TestMethod]
	public void SafelistSkipsHaveWarningsButNoSourcePath()
	{
		var config = TailBoxConfig.CreateDefault();
		config.Safelist.Add( "grid unknown:flex" );

		var result = new TailBoxGenerator().GenerateFromSources( Array.Empty<TailBoxSourceText>(), config );

		CollectionAssert.AreEqual( new[] { "grid", "unknown:flex" }, result.SkippedClasses.ToArray() );
		Assert.IsTrue( result.Skipped.All( item => item.SourcePath is null ) );
		Assert.IsTrue( result.Warnings.Any( warning => warning.StartsWith( "grid:", StringComparison.Ordinal ) ) );
		Assert.IsTrue( result.Warnings.Any( warning => warning.StartsWith( "unknown:flex:", StringComparison.Ordinal ) ) );
	}

	[TestMethod]
	public void ProjectPathGeneratorMethodsStayEditorOnly()
	{
		var generator = new TailBoxGenerator();
		var config = TailBoxConfig.CreateDefault();

		Assert.ThrowsException<NotSupportedException>( () => generator.Generate( "C:/Project" ) );
		Assert.ThrowsException<NotSupportedException>( () => generator.Generate( "C:/Project", config ) );
		Assert.ThrowsException<NotSupportedException>( () => generator.ScanClasses( "C:/Project", config ) );
		Assert.ThrowsException<NotSupportedException>( () => generator.FindContentFiles( "C:/Project", config ) );
	}
}