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

namespace Connected.Components;

public partial class Chip : UIComponent, IDisposable
{
	private bool _isSelected;
	[Inject] public NavigationManager UriHelper { get; set; }

	[Inject] public IJsApiService JsApiService { get; set; }

	protected string Classname =>
	new CssBuilder("chip")
	  .AddClass($"chip-{GetVariant().ToDescription()}")
	  .AddClass($"chip-size-{Size.ToDescription()}")
	  .AddClass($"chip-color-{GetColor().ToDescription()}")
	  .AddClass("clickable", !ChipSet?.ReadOnly ?? OnClick.HasDelegate)
	  .AddClass("ripple", !ChipSet?.ReadOnly ?? OnClick.HasDelegate && !DisableRipple)
	  .AddClass("chip-label", Label)
	  .AddClass("disabled", Disabled)
	  .AddClass("chip-selected", IsSelected)
	  .AddClass(AdditionalClassList)
	.Build();

	//Cannot test the get variant (last line)
	[ExcludeFromCodeCoverage]
	private Variant GetVariant()
	{
		return Variant switch
		{
			Variant.Text => IsSelected ? Variant.Filled : Variant.Text,
			Variant.Filled => IsSelected ? Variant.Text : Variant.Filled,
			Variant.Outlined => Variant.Outlined,
			_ => Variant
		};
	}

	private ThemeColor GetColor()
	{
		if (IsSelected && SelectedColor != ThemeColor.Inherit)
		{
			return SelectedColor;
		}
		else if (IsSelected && SelectedColor == ThemeColor.Inherit)
		{
			return Color;
		}
		else
		{
			return Color;
		}
	}

	[CascadingParameter] ChipSet ChipSet { get; set; }

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

	/// <summary>
	/// The size of the button. small is equivalent to the dense button styling.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public Size Size { get; set; } = Size.Medium;

	/// <summary>
	/// The variant to use.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public Variant Variant { get; set; } = Variant.Filled;

	/// <summary>
	/// The selected color to use when selected, only works together with ChipSet, Color.Inherit for default value.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public ThemeColor SelectedColor { get; set; } = ThemeColor.Inherit;

	/// <summary>
	/// Avatar Glyph, Overrides the regular Glyph if set.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Behavior)]
	public string Avatar { get; set; }

	/// <summary>
	/// Avatar CSS Class, appends to Chips default avatar classes.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public string AvatarClass { get; set; }

	/// <summary>
	/// Removes circle edges and applies theme default.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public bool Label { get; set; }

	/// <summary>
	/// If true, the chip will be displayed in disabled state and no events possible.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Behavior)]
	public bool Disabled { get; set; }

	/// <summary>
	/// Sets the Glyph to use.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Behavior)]
	public string Icon { get; set; }

	/// <summary>
	/// Custom checked icon.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public string CheckedIcon { get; set; } = Icons.Material.Filled.Check;

	/// <summary>
	/// The color of the icon.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;

	/// <summary>
	/// Overrides the default close icon, only shown if OnClose is set.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public string CloseIcon { get; set; }

	/// <summary>
	/// If true, disables ripple effect, ripple effect is only applied to clickable chips.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Appearance)]
	public bool DisableRipple { get; set; }

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

	/// <summary>
	/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where (Obsolete replaced by Href)
	/// </summary>
	[Obsolete("Use Href Instead.", false)]
	[Parameter]
	[Category(CategoryTypes.Chip.ClickAction)]
	public string Link
	{
		get => Href;
		set => Href = value;
	}

	/// <summary>
	/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.ClickAction)]
	public string Href { get; set; }

	/// <summary>
	/// The target attribute specifies where to open the link, if Href is specified. Possible values: _blank | _self | _parent | _top | <i>framename</i>
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.ClickAction)]
	public string Target { get; set; }

	/// <summary>
	/// A string you want to associate with the chip. If the ChildContent is not set this will be shown as chip text.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Behavior)]
	public string Text { get; set; }

	/// <summary>
	/// A value that should be managed in the SelectedValues collection.
	/// Note: do not change the value during the chip's lifetime
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Behavior)]
	public object Value { get; set; }

	/// <summary>
	/// If true, force browser to redirect outside component router-space.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.ClickAction)]
	public bool ForceLoad { get; set; }

	/// <summary>
	/// If true, this chip is selected by default if used in a ChipSet. 
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.Behavior)]
	public bool? Default { get; set; }

	/// <summary>
	/// Command executed when the user clicks on an element.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.ClickAction)]
	public ICommand Command { get; set; }

	/// <summary>
	/// Command parameter.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Chip.ClickAction)]
	public object CommandParameter { get; set; }

	/// <summary>
	/// Chip click event, if set the chip focus, hover and click effects are applied.
	/// </summary>
	[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }

	/// <summary>
	/// Chip delete event, if set the delete icon will be visible.
	/// </summary>
	[Parameter] public EventCallback<Chip> OnClose { get; set; }

	/// <summary>
	/// Set by MudChipSet
	/// </summary>
	public bool IsChecked
	{
		get => _isSelected && ChipSet?.Filter == true;
	}

	/// <summary>
	/// If false, this chip has not been seen before
	/// </summary>
	public bool DefaultProcessed { get; set; }

	/// <summary>
	/// Set by MudChipSet
	/// </summary>
	public bool IsSelected
	{
		get => _isSelected;
		set
		{
			if (_isSelected == value)
				return;
			_isSelected = value;
			StateHasChanged();
		}
	}

	protected override void OnInitialized()
	{
		base.OnInitialized();
		if (Value == null)
			Value = this;
	}

	protected internal async Task OnClickHandler(MouseEventArgs ev)
	{
		if (ChipSet?.ReadOnly == true)
		{
			return;
		}
		if (ChipSet != null)
		{
			_ = ChipSet.OnChipClicked(this);
		}
		if (Href != null)
		{
			// TODO: use MudElement to render <a> and this code can be removed. we know that it has potential problems on iOS
			if (string.IsNullOrWhiteSpace(Target))
				UriHelper.NavigateTo(Href, ForceLoad);
			else
				await JsApiService.Open(Href, Target);
		}
		else
		{
			await OnClick.InvokeAsync(ev);
			if (Command?.CanExecute(CommandParameter) ?? false)
			{
				Command.Execute(CommandParameter);
			}
		}
	}

	protected async Task OnCloseHandler(MouseEventArgs ev)
	{
		if (ChipSet?.ReadOnly == true)
		{
			return;
		}
		await OnClose.InvokeAsync(this);
		ChipSet?.OnChipDeleted(this);
		StateHasChanged();
	}

	protected override Task OnInitializedAsync()
	{
		ChipSet?.Add(this);
		return base.OnInitializedAsync();
	}

	internal void ForceRerender() => StateHasChanged();

	//Exclude because we don't test to catching exception yet
	[ExcludeFromCodeCoverage]
	public void Dispose()
	{
		try
		{
			ChipSet?.Remove(this);
		}
		catch (Exception)
		{
			/* ignore! */
		}
	}

}
