using System;
using KitsuneCafe.Entities;
using KitsuneCafe.SOAP;
using R3;
using UnityEngine;

namespace KitsuneCafe.ItemSystem
{
  public record ChangedEquipmentEvent(IEquippable Previous, IEquippable Current);

  public class EquipmentInstance : MonoBehaviour
  {
    [SerializeField]
    private InventoryInstance inventory;

    [SerializeField]
    private BoneAttachment attachment;

    [SerializeField]
    private EquipmentValue primary;
    public EquipmentItem Primary => primary.Value;

    [SerializeField]
    private EquipmentValue secondary;
    public EquipmentItem Secondary => secondary.Value;

    private IEquippable equippedPrimary;

    public event EventHandler<ChangedEquipmentEvent> ChangedEquipment;
    public event EventHandler<bool> Readied;
    public event EventHandler Attacked;
    public event EventHandler Reloaded;

    public bool HasPrimaryEquipped => primary != null;
    public bool HasSecondaryEquipped => secondary != null;

    private void OnValidate()
    {
      if (inventory == null)
      {
        inventory = GetComponent<InventoryInstance>();
      }

      if (attachment == null)
      {
        attachment = GetComponent<BoneAttachment>();
      }
    }

    private void Start()
    {
      var d = Disposable.CreateBuilder();

      primary.AsObservable()
        .Subscribe(EquipPrimary)
        .AddTo(ref d);

      secondary.AsObservable()
        .Subscribe()
        .AddTo(ref d);

      d.RegisterTo(destroyCancellationToken);
    }

    private void EquipPrimary(EquipmentItem equipment)
    {
      var previous = equippedPrimary;

      if (equippedPrimary != null)
      {
        equippedPrimary.OnUnequip();
        equippedPrimary.Readied -= OnReady;
        equippedPrimary.Attacked -= OnAttack;
        equippedPrimary.Reloaded -= OnReload;

        Destroy(equippedPrimary.gameObject);
        equippedPrimary = null;
      }

      IEquippable instance = null;
      if (equipment != null)
      {
        instance = equipment.CreateEquipment();
        instance.Readied += OnReady;
        instance.Attacked += OnAttack;
        instance.Reloaded += OnReload;
        instance.gameObject.transform.SetParent(attachment.transform, false);
        equippedPrimary = instance;

        equippedPrimary.OnEquip(inventory);
      }

      ChangedEquipment?.Invoke(this, new ChangedEquipmentEvent(previous, instance));
    }

    private void OnReload(object sender, EventArgs e)
    {
      Reloaded?.Invoke(sender, e);
    }

    private void OnAttack(object sender, EventArgs e)
    {
      Attacked?.Invoke(sender, e);
    }

    private void OnReady(object sender, bool e)
    {
      Readied?.Invoke(sender, e);
    }
  }
}
