using UnityEngine;
using UnityEngine.Events;

using Photon.Pun;

using UPG.Extensions;

namespace BR.BattleRoyale.Items
{
	[RequireComponent(typeof(MeshFilter))]
	[RequireComponent(typeof(MeshRenderer))]
	public class Potion : Item
	{
		#region EVENTS
		[HideInInspector] public IntEvent OnStackSizeUpdated = new IntEvent();
		[HideInInspector] public UnityEvent OnStackDepleted = new UnityEvent();
		[HideInInspector] public UnityEvent OnDrinkStarted = new UnityEvent();
		#endregion

        #region INSPECTOR FIELDS
        [Header("Data")]
		[SerializeField] PotionData m_potionData;
        #endregion

		#region PRIVATE FIELDS
		BoundedInt m_stackCount = new BoundedInt(1, 1, 0);
		#endregion

		#region READABLES
		public string DisplayName =>
			m_potionData ? m_potionData.DisplayName : gameObject.name;
		public PotionData.EffectType Effect =>
			m_potionData ? m_potionData.Effect : PotionData.EffectType.Undefined;
		public Rarity Rarity =>
			m_potionData ? m_potionData.Rarity : Rarity.Basic;
		public int StackCount =>
			m_stackCount.Value;
		public int StackCapacityRemaining =>
			m_stackCount.MaximumValue - m_stackCount.Value;
		public float Value =>
			m_potionData ? m_potionData.Value : 0;
		#endregion

		#region DEFAULT METHODS
		new void OnValidate()
		{
			base.OnValidate();

			if (!m_potionData)
			{
				if (gameObject.name != "Mystery Potion")
					gameObject.name = "Mystery Potion";

				return;
			}

			if (!gameObject.GetIsPrefab() && gameObject.name != m_potionData.name)
				gameObject.name = m_potionData.name;

			if (m_potionData.MaxStackSize != m_stackCount.MaximumValue)
				m_stackCount = new BoundedInt(1, m_potionData.MaxStackSize, 0);

			if (m_potionData && MeshFilter.sharedMesh != m_potionData.Mesh)
				MeshFilter.sharedMesh = m_potionData.Mesh;
		}
		new void Awake()
		{
			base.Awake();

			if (!m_potionData)
			{
				Destroy(this);
				return;
			}

			m_stackCount = new BoundedInt(1, m_potionData.MaxStackSize, 0);

			m_stackCount.OnValueUpdated.AddListener((BoundedInt _value) => {
				m_debugChannel?.Raise(this, name + " has " + _value.Value + " uses remaining");
			});

            m_stackCount.OnValueMinimum.AddListener(() => { 
				OnStackDepleted?.Invoke();

				m_debugChannel?.Raise(this, name + " depleted");

				if (photonView && PhotonNetwork.IsConnected && PhotonNetwork.InRoom)
					PhotonNetwork.Destroy(photonView);
				else
					Destroy(gameObject);
			});
        }
		#endregion

		#region OVERRIDE METHODS
		public override void StartPrimary() 
		{
			OnDrinkStarted?.Invoke();
		}
        public override void StartSecondary() { /* Throw the potion? */ }
        public override void PickUp(Player _player)
        {
			if (!_player)
				return;

			Item[] _existingPotionStacks = _player.Inventory.FindAll(x => x != null && IsAddablePotion(x.ToPotion())).ToArray();

			if (_existingPotionStacks.Length != 0)
			{
				for (int i = 0; i < _existingPotionStacks.Length && m_stackCount.Percentage > 0; i++)
                {
					int _amountAdded = _existingPotionStacks[i].ToPotion().TryAddToStack(m_stackCount.Value);

					m_debugChannel?.Raise(this, _amountAdded + " added to " + _existingPotionStacks[i].name);

					m_stackCount -= _amountAdded;

					m_debugChannel?.Raise(this, m_stackCount.Value + " remaining to assign");
                }

				if (m_stackCount.Percentage == 0)
					return;
			}

			Owner = _player;

			m_sphereCollider.enabled = false;

			OnItemPickedUp?.Invoke();
		}
        #endregion

        #region ADD POTION METHODS
        bool IsAddablePotion(Potion _potion)
        {
			if (!_potion)
				return false;

			if (_potion.Effect != Effect)
				return false;

			if (_potion.StackCapacityRemaining <= 0)
				return false;

			return true;
        }
        /// <summary>
        /// Returns the number of potions successfully added to the stack.
        /// </summary>
        /// <param name="_amount"></param>
        /// <returns></returns>
        public int TryAddToStack(int _amount)
        {
			if (m_stackCount.Percentage == 1)
				return 0;

			return (m_stackCount += _amount <= m_stackCount.MaximumValue - m_stackCount.Value ? _amount : m_stackCount.MaximumValue - m_stackCount.Value).Value;
		}
		#endregion

		#region CONSUME METHODS
		public void ApplyTo(Player _player)
		{
			if (!_player)
				return;

			m_debugChannel?.Raise(this, "Applying to " + _player.name);

			switch (m_potionData.Effect)
			{
				case PotionData.EffectType.Health:
					_player.Health.Value += Value;
					break;
				case PotionData.EffectType.Mana:
					_player.Mana.Value += Value;
					break;
				case PotionData.EffectType.Resource:
					_player.BuildEnergy.Value += Value;
					break;
				case PotionData.EffectType.Undefined:
				default:
					break;
			}

			m_debugChannel?.Raise(this, new string[] { "Reducing stack size by 1", "Currently has " + m_stackCount.Value });

			m_stackCount.Value -= 1;
		}
        #endregion
    }
}