using Connected.Annotations;
using Connected.Extensions;
using Connected.Services;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Connected.Components;

public partial class CheckBox<T> : BooleanInput<T>
{
	protected string Classname =>
	new CssBuilder("input-control-boolean-input")
		.AddClass(AdditionalClassList)
		.Build();

	protected string LabelClassname =>
	new CssBuilder("checkbox")
		.AddClass($"disabled", Disabled)
		.AddClass($"readonly", ReadOnly)
		.AddClass(LabelPosition == LabelPosition.End ? "ltr" : "rtl", true)
	.Build();

	protected string CheckBoxClassname =>
	new CssBuilder("button-root mud-icon-button")
		.AddClass($"{Color.ToDescription()}-text hover:mud-{Color.ToDescription()}-hover", UnCheckedColor == null || (UnCheckedColor != null && BoolValue == true))
		.AddClass($"{UnCheckedColor?.ToDescription()}-text hover:mud-{UnCheckedColor?.ToDescription()}-hover", UnCheckedColor != null && BoolValue == false)
		.AddClass($"checkbox-dense", Dense)
		.AddClass($"ripple mud-ripple-checkbox", !DisableRipple && !ReadOnly && !Disabled)
		.AddClass($"disabled", Disabled)
		.AddClass($"readonly", ReadOnly)
	.Build();

	/// <summary>
	/// The color of the component. It supports the theme colors.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public ThemeColor Color { get; set; } = ThemeColor.Default;

	/// <summary>
	/// The base color of the component in its none active/unchecked state. It supports the theme colors.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Radio.Appearance)]
	public ThemeColor? UnCheckedColor { get; set; } = null;

	/// <summary>
	/// The text/label will be displayed next to the checkbox if set.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Behavior)]
	public string Label { get; set; }

	/// <summary>
	/// The position of the text/label.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Behavior)]
	public LabelPosition LabelPosition { get; set; } = LabelPosition.End;

	/// <summary>
	/// If true, the checkbox can be controlled with the keyboard.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Behavior)]
	public bool KeyboardEnabled { get; set; } = true;

	/// <summary>
	/// If true, disables ripple effect.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public bool DisableRipple { get; set; }

	/// <summary>
	/// If true, compact padding will be applied.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public bool Dense { get; set; }

	/// <summary>
	/// The Size of the component.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public Size Size { get; set; } = Size.Medium;

	/// <summary>
	/// Child content of component.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Behavior)]
	public RenderFragment ChildContent { get; set; }

	/// <summary>
	/// Custom checked icon, leave null for default.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public string CheckedIcon { get; set; } = Icons.Material.Filled.CheckBox;

	/// <summary>
	/// Custom unchecked icon, leave null for default.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public string UncheckedIcon { get; set; } = Icons.Material.Filled.CheckBoxOutlineBlank;

	/// <summary>
	/// Custom indeterminate icon, leave null for default.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Appearance)]
	public string IndeterminateIcon { get; set; } = Icons.Material.Filled.IndeterminateCheckBox;

	/// <summary>
	/// Define if the checkbox can cycle again through indeterminate status.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.FormComponent.Validation)]
	public bool TriState { get; set; }

	private string GetIcon()
	{
		if (BoolValue == true)
		{
			return CheckedIcon;
		}

		if (BoolValue == false)
		{
			return UncheckedIcon;
		}

		return IndeterminateIcon;
	}

	protected override Task OnChange(ChangeEventArgs args)
	{
		Modified = true;

		// Apply only when TriState parameter is set to true and T is bool?
		if (TriState && typeof(T) == typeof(bool?))
		{
			// The cycle is forced with the following steps: true, false, indeterminate, true, false, indeterminate...
			if (!((bool?)(object)_value).HasValue)
			{
				return SetBoolValueAsync(true);
			}
			else
			{
				return ((bool?)(object)_value).Value ? SetBoolValueAsync(false) : SetBoolValueAsync(default);
			}
		}
		else
		{
			return SetBoolValueAsync((bool?)args.Value);
		}
	}

	protected void HandleKeyDown(KeyboardEventArgs obj)
	{
		if (Disabled || ReadOnly || !KeyboardEnabled)
			return;
		switch (obj.Key)
		{
			case "Delete":
				SetBoolValueAsync(false);
				break;
			case "Enter":
			case "NumpadEnter":
				SetBoolValueAsync(true);
				break;
			case "Backspace":
				if (TriState)
				{
					SetBoolValueAsync(null);
				}
				break;
			case " ":
				if (BoolValue == null)
				{
					SetBoolValueAsync(true);
				}
				else if (BoolValue == true)
				{
					SetBoolValueAsync(false);
				}
				else if (BoolValue == false)
				{
					if (TriState == true)
					{
						SetBoolValueAsync(null);
					}
					else
					{
						SetBoolValueAsync(true);
					}
				}
				break;
		}
	}

	private IKeyInterceptor _keyInterceptor;
	[Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; }

	private string _elementId = "checkbox" + Guid.NewGuid().ToString().Substring(0, 8);

	protected override void OnInitialized()
	{
		base.OnInitialized();

		if (Label == null && For != null)
			Label = For.GetLabelString();
	}

	protected override async Task OnAfterRenderAsync(bool firstRender)
	{
		if (firstRender)
		{
			_keyInterceptor = _keyInterceptorFactory.Create();

			await _keyInterceptor.Connect(_elementId, new KeyInterceptorOptions()
			{
				//EnableLogging = true,
				TargetClass = "button-root",
				Keys = {
					 new KeyOptions { Key=" ", PreventDown = "key+none", PreventUp = "key+none" }, // prevent scrolling page
                     new KeyOptions { Key="Enter", PreventDown = "key+none" },
					 new KeyOptions { Key="NumpadEnter", PreventDown = "key+none" },
					 new KeyOptions { Key="Backspace", PreventDown = "key+none" },
				 },
			});
			_keyInterceptor.KeyDown += HandleKeyDown;
		}
		await base.OnAfterRenderAsync(firstRender);
	}

	protected override void Dispose(bool disposing)
	{
		base.Dispose(disposing);

		if (disposing == true)
		{
			if (_keyInterceptor != null)
			{
				_keyInterceptor.KeyDown -= HandleKeyDown;
				_keyInterceptor.Dispose();
			}
		}
	}
}
