using Connected.Annotations;
using Connected.Utilities;
using Connected.Utilities.Exceptions;
using Microsoft.AspNetCore.Components;

namespace Connected.Components;

public partial class RadioGroup<T> : FormComponent<T, T>, IRadioGroup
{
   public RadioGroup() : base(new Converter<T, T>()) { }

   private Radio<T> _selectedRadio;

   private HashSet<Radio<T>> _radios = new();

   protected string Classname =>
   new CssBuilder("input-control-boolean-input")
       .AddClass(AdditionalClassList)
       .Build();

   private string GetInputClass() =>
   new CssBuilder("radio-group")
       .AddClass(InputClass)
       .Build();

   /// <summary>
   /// User class names for the input, separated by space
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.Radio.Appearance)]
   public string InputClass { get; set; }

   /// <summary>
   /// User style definitions for the input
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.Radio.Appearance)]
   public string InputStyle { get; set; }

   [Parameter]
   [Category(CategoryTypes.Radio.Behavior)]
   public RenderFragment ChildContent { get; set; }

   [Parameter]
   [Category(CategoryTypes.Radio.Behavior)]
   public string Name { get; set; } = Guid.NewGuid().ToString();

   public void CheckGenericTypeMatch(object select_item)
   {
      var itemT = select_item.GetType().GenericTypeArguments[0];
      if (itemT != typeof(T))
         throw new GenericTypeMismatchException("MudRadioGroup", "MudRadio", typeof(T), itemT);
   }

   [Parameter]
   [Category(CategoryTypes.Radio.Data)]
   public T SelectedOption
   {
      get => _value;
      set => SetSelectedOptionAsync(value, true).AndForget();
   }

   protected async Task SetSelectedOptionAsync(T option, bool updateRadio)
   {
      if (!OptionEquals(_value, option))
      {
         _value = option;

         if (updateRadio)
            await SetSelectedRadioAsync(_radios.FirstOrDefault(r => OptionEquals(r.Option, _value)), false);

         await SelectedOptionChanged.InvokeAsync(_value);

         BeginValidate();
         FieldChanged(_value);
      }
   }

   [Parameter]
   public EventCallback<T> SelectedOptionChanged { get; set; }

   internal Task SetSelectedRadioAsync(Radio<T> radio)
   {
      Modified = true;
      return SetSelectedRadioAsync(radio, true);
   }

   protected async Task SetSelectedRadioAsync(Radio<T> radio, bool updateOption)
   {
      if (_selectedRadio != radio)
      {
         _selectedRadio = radio;

         foreach (var item in _radios.ToArray())
            item.SetChecked(item == _selectedRadio);

         if (updateOption)
            await SetSelectedOptionAsync(GetOptionOrDefault(_selectedRadio), false);
      }
   }

   internal Task RegisterRadioAsync(Radio<T> radio)
   {
      _radios.Add(radio);

      if (_selectedRadio == null)
      {
         if (OptionEquals(radio.Option, _value))
            return SetSelectedRadioAsync(radio, false);
      }
      return Task.CompletedTask;
   }

   internal void UnregisterRadio(Radio<T> radio)
   {
      _radios.Remove(radio);

      if (_selectedRadio == radio)
         _selectedRadio = null;
   }

   protected override void ResetValue()
   {
      if (_selectedRadio != null)
      {
         _selectedRadio.SetChecked(false);
         _selectedRadio = null;
      }

      base.ResetValue();
   }

   private static T GetOptionOrDefault(Radio<T> radio)
   {
      return radio != null ? radio.Option : default;
   }

   private static bool OptionEquals(T option1, T option2)
   {
      return EqualityComparer<T>.Default.Equals(option1, option2);
   }
}
