using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Web;

namespace Connected.Components;

/// <summary>
/// Primitive component which allows rendering any HTML element we want
/// through the HtmlTag property
/// </summary>
public class Element : UIComponent
{
	/// <summary>
	/// Child content
	/// </summary>
	[Parameter]
	public RenderFragment? ChildContent { get; set; }

	#region EventCallbacks
	[Parameter]
	public EventCallback<ElementReference> RefChanged { get; set; }
	#endregion

	#region Content placeholders
	/// <summary>
	/// The Ref to bind to.
	/// Usage: @bind-Ref="myRef"
	/// </summary>
	[Parameter]
	public ElementReference? Ref { get; set; }
	#endregion

	#region Styling properties
	/// <summary>
	/// A space separated list of class names, added on top of the default class list.
	/// </summary>
	[Parameter]
	public string? ClassList { get; set; }
	#endregion

	#region Behavior properties
	/// <summary>
	/// The HTML element that will be rendered in the root by the component
	/// </summary>
	[Parameter]
	public string HtmlTag { get; set; } = "span";

	[Parameter]
	public bool PreventOnClickPropagation { get; set; }
	#endregion

	/// <summary>
	/// Schedules a component's state refresh
	/// </summary>
	public void Refresh() => StateHasChanged();

	protected override void BuildRenderTree(RenderTreeBuilder builder)
	{
		base.BuildRenderTree(builder);

		builder.OpenElement(0, HtmlTag);
	
		/*
		 * Handle splatted attributes
		 */
		foreach (var attribute in CustomAttributes)
		{
			/* 
			 * Checking if the value is null, we can get rid of null event handlers,
			 * for example `@onmouseenter=@(IsOpen ? HandleEnter : null)`.
			 * This is a powerful feature that does not work in regular html elements
			 * because Blazor always adds the attribute value and creates an EventCallback.
			 * */
			if (attribute.Value is not null)
				builder.AddAttribute(1, attribute.Key, attribute.Value);
		}

		builder.AddAttribute(2, "class", ClassList);

		/*
		 * Stop onclock propagation for buttons. The order matters.
		 * This has to be before content is added
		 */
		if (PreventOnClickPropagation)
			builder.AddEventStopPropagationAttribute(5, "onclick", true);

		/*
		 * Add reference capture propagation.
		 */
		if (Ref is not null)
		{
			builder.AddElementReferenceCapture(6, async capturedRef =>
			{
				Ref = capturedRef;
				await RefChanged.InvokeAsync(Ref.Value);
			});
		}

		/*
		 * Render child content
		 */
		builder.AddContent(10, ChildContent);

		builder.CloseElement();
	}
}
