// Copyright (c) 2011 - 2019 Ed Charbeneau
// License: MIT
// See https://github.com/EdCharbeneau

using System.Text;

namespace Connected.Utilities;

public readonly struct CssBuilder
{
	private readonly StringBuilder _stringBuffer;

	public CssBuilder() => _stringBuffer = new StringBuilder();
	/// <summary>
	/// Creates a CssBuilder used to define conditional CSS classes used in a component.
	/// Call Build() to return the completed CSS Classes as a string. 
	/// </summary>
	/// <param name="value"></param>
	public CssBuilder(string value) => _stringBuffer = new StringBuilder(value?.ToLower());

	/// <summary>
	/// Adds a raw string to the builder that will be concatenated with the next class or value added to the builder. All values are converted to lowercase.
	/// </summary>
	/// <param name="value"></param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddValue(string value)
	{
		_stringBuffer.Append(value?.ToLower());
		return this;
	}

	/// <summary>
	/// Adds a CSS Class to the builder with space separator. All classes are converted to lowercase.
	/// </summary>
	/// <param name="value">CSS Class to add</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(string? value)
	{
		if (value is null)
			return this;

		return AddValue($" {value?.ToLower()}");
	}

	/// <summary>
	/// Adds a conditional CSS Class to the builder with space separator.
	/// </summary>
	/// <param name="value">CSS Class to conditionally add.</param>
	/// <param name="when">Condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(string value, bool when = true) => when ? AddClass(value) : this;

	/// <summary>
	/// Adds a conditional CSS Class to the builder with space separator.
	/// </summary>
	/// <param name="value">CSS Class to conditionally add.</param>
	/// <param name="when">Nullable condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(string value, bool? when = true) => when == true ? AddClass(value) : this;

	/// <summary>
	/// Adds a conditional CSS Class to the builder with space separator.
	/// </summary>
	/// <param name="value">CSS Class to conditionally add.</param>
	/// <param name="when">Condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(string value, Func<bool>? when = null) => AddClass(value, when is not null && when());

	/// <summary>
	/// Adds a conditional CSS Class to the builder with space separator.
	/// </summary>
	/// <param name="value">Function that returns a CSS Class to conditionally add.</param>
	/// <param name="when">Condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(Func<string> value, bool when = true) => when ? AddClass(value()) : this;

	/// <summary>
	/// Adds a conditional CSS Class to the builder with space separator.
	/// </summary>
	/// <param name="value">Function that returns a CSS Class to conditionally add.</param>
	/// <param name="when">Condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(Func<string> value, Func<bool> when = null) => AddClass(value, when != null && when());

	/// <summary>
	/// Adds a conditional nested CssBuilder to the builder with space separator.
	/// </summary>
	/// <param name="builder">CSS Class to conditionally add.</param>
	/// <param name="when">Condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(CssBuilder builder, bool when = true) => when ? AddClass(builder.Build()) : this;

	/// <summary>
	/// Adds a conditional CSS Class to the builder with space separator.
	/// </summary>
	/// <param name="builder">CSS Class to conditionally add.</param>
	/// <param name="when">Condition in which the CSS Class is added.</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClass(CssBuilder builder, Func<bool> when = null) => AddClass(builder, when != null && when());

	/// <summary>
	/// Adds a conditional CSS Class when it exists in a dictionary to the builder with space separator.
	/// Null safe operation.
	/// </summary>
	/// <param name="additionalAttributes">Additional Attribute splat parameters</param>
	/// <returns>CssBuilder</returns>
	public CssBuilder AddClassFromAttributes(IReadOnlyDictionary<string, object> additionalAttributes) =>
		 additionalAttributes is null ? this :
		 additionalAttributes.TryGetValue("class", out var c) ? AddClass(c.ToString()) : this;

	/// <summary>
	/// Finalize the completed CSS Classes as a string.
	/// </summary>
	/// <returns>string</returns>
	public string Build() => _stringBuffer.ToString().Trim();

	// ToString should only and always call Build to finalize the rendered string.
	public override string ToString() => Build();
}
