using System.Diagnostics.CodeAnalysis;
using Connected.Annotations;
using Connected.Extensions;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;

namespace Connected.Components;

public partial class TreeView<T> : UIComponent
{
	private TreeViewItem<T> _selectedValue;
	private HashSet<TreeViewItem<T>> _selectedValues;
	private List<TreeViewItem<T>> _childItems = new();

	protected string Classname =>
	new CssBuilder("treeview")
	  .AddClass("treeview-dense", Dense)
	  .AddClass("treeview-hover", Hover)
	  .AddClass($"treeview-selected-{Color.ToDescription()}")
		.AddClass($"treeview-checked-{CheckBoxColor.ToDescription()}")
	  .AddClass(AdditionalClassList)
	.Build();
	protected string Stylename =>
	new StyleBuilder()
		.AddStyle($"width", Width, !string.IsNullOrWhiteSpace(Width))
		.AddStyle($"height", Height, !string.IsNullOrWhiteSpace(Height))
		.AddStyle($"max-height", MaxHeight, !string.IsNullOrWhiteSpace(MaxHeight))
	.Build();

	/// <summary>
	/// The color of the selected treeviewitem.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Selecting)]
	public ThemeColor Color { get; set; } = ThemeColor.Primary;

	/// <summary>
	/// Check box color if multiselection is used.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Selecting)]
	public ThemeColor CheckBoxColor { get; set; }

	/// <summary>
	/// if true, multiple values can be selected via checkboxes which are automatically shown in the tree view.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Selecting)]
	public bool MultiSelection { get; set; }

	/// <summary>
	/// if true, multiple values can be selected via checkboxes which are automatically shown in the tree view.
	/// </summary>
	[ExcludeFromCodeCoverage]
	[Obsolete("Use MultiSelection instead.", true)]
	[Parameter]
	public bool CanSelect
	{
		get => MultiSelection;
		set => MultiSelection = value;
	}

	[ExcludeFromCodeCoverage]
	[Obsolete("MudTreeView now automaticly activates when using SelectedValue.", true)]
	[Parameter] public bool CanActivate { get; set; }

	/// <summary>
	/// If true, clicking anywhere on the item will expand it, if it has childs.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.ClickAction)]
	public bool ExpandOnClick { get; set; }

	/// <summary>
	/// If true, double clicking anywhere on the item will expand it, if it has childs.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.ClickAction)]
	public bool ExpandOnDoubleClick { get; set; }

	/// <summary>
	/// Hover effect for item's on mouse-over.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Appearance)]
	public bool Hover { get; set; }

	/// <summary>
	/// Hover effect for item's on mouse-over.
	/// </summary>
	[ExcludeFromCodeCoverage]
	[Obsolete("Use Hover instead.", true)]
	[Parameter]
	public bool CanHover
	{
		get => Hover;
		set => Hover = value;
	}

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

	/// <summary>
	/// Setting a height will allow to scroll the treeview. If not set, it will try to grow in height. 
	/// You can set this to any CSS value that the attribute 'height' accepts, i.e. 500px. 
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Appearance)]
	public string Height { get; set; }

	/// <summary>
	/// Setting a maximum height will allow to scroll the treeview. If not set, it will try to grow in height. 
	/// You can set this to any CSS value that the attribute 'height' accepts, i.e. 500px. 
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Appearance)]
	public string MaxHeight { get; set; }

	/// <summary>
	/// Setting a width the treeview. You can set this to any CSS value that the attribute 'height' accepts, i.e. 500px. 
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Appearance)]
	public string Width { get; set; }

	/// <summary>
	/// If true, treeview will be disabled and all its childitems.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Behavior)]
	public bool Disabled { get; set; }

	[Parameter]
	[Category(CategoryTypes.TreeView.Data)]
	public HashSet<T> Items { get; set; }

	[ExcludeFromCodeCoverage]
	[Obsolete("Use SelectedValueChanged instead.", true)]
	[Parameter]
	public EventCallback<T> ActivatedValueChanged
	{
		get => SelectedValueChanged;
		set => SelectedValueChanged = value;
	}

	/// <summary>
	/// Called whenever the selected value changed.
	/// </summary>
	[Parameter] public EventCallback<T> SelectedValueChanged { get; set; }

	/// <summary>
	/// Called whenever the selectedvalues changed.
	/// </summary>
	[Parameter] public EventCallback<HashSet<T>> SelectedValuesChanged { get; set; }

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

	/// <summary>
	/// ItemTemplate for rendering children.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.TreeView.Data)]
	public RenderFragment<T> ItemTemplate { get; set; }

	[CascadingParameter] TreeView<T> MudTreeRoot { get; set; }

	[Parameter]
	[Category(CategoryTypes.TreeView.Data)]
	public Func<T, Task<HashSet<T>>> ServerData { get; set; }

	public TreeView()
	{
		MudTreeRoot = this;
	}

	internal bool IsSelectable { get; private set; }

	protected override void OnInitialized()
	{
		IsSelectable = SelectedValueChanged.HasDelegate;
	}

	protected override async Task OnAfterRenderAsync(bool firstRender)
	{
		if (firstRender && MudTreeRoot == this)
		{
			await UpdateSelectedItems();
		}
		await base.OnAfterRenderAsync(firstRender);
	}

	internal async Task UpdateSelected(TreeViewItem<T> item, bool requestedValue)
	{
		if ((_selectedValue == item && requestedValue) ||
			(_selectedValue != item && !requestedValue))
			return;

		if (_selectedValue == item && !requestedValue)
		{
			_selectedValue = default;
			await item.Select(requestedValue);
			await SelectedValueChanged.InvokeAsync(default);
			return;
		}

		if (_selectedValue != null)
		{
			await _selectedValue.Select(false);
		}

		_selectedValue = item;
		await item.Select(requestedValue);
		await SelectedValueChanged.InvokeAsync(item.Value);
	}

	internal Task UpdateSelectedItems()
	{
		_selectedValues ??= new HashSet<TreeViewItem<T>>();

		//collect selected items
		_selectedValues.Clear();
		foreach (var item in _childItems)
		{
			foreach (var selectedItem in item.GetSelectedItems())
			{
				_selectedValues.Add(selectedItem);
			}
		}

		return SelectedValuesChanged.InvokeAsync(new HashSet<T>(_selectedValues.Select(i => i.Value)));
	}

	internal void AddChild(TreeViewItem<T> item) => _childItems.Add(item);
}
