using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract;

namespace VOID.Generic.Objects.Item
{
    public class ItemObject : AbstractObject
    {
        [Title("=== ItemObject properties ===")]
        public ItemObjectData itemData;
        public ItemObjectEvents itemEvents;
        public ItemObjectState itemState;

        protected override List<ObjectComponent> GetObjectComponents()
        {
            return base.GetObjectComponents()
                .Union(new List<ObjectComponent>
                {
                    itemData,
                    itemEvents,
                    itemState
                })
                .ToList();
        }

        /// <summary>
        /// Moves current item to given position
        /// </summary>
        /// <returns>TRUE if item can be moved</returns>
        public bool Move(Vector3 moveTo)
        {
            // TODO: póki co przenoszenie za pomocą "teleportacji", kiedyś dorobić ładny ruch przedmiotu do tego miejsca
            Debug.LogWarning("TODO: Action MoveItem: item teleported");
            transform.position = moveTo;
            transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y, 0);
            return true;
        }

        public bool CanStackWith(ItemObject otherItem) => CanStackWith(otherItem, out _);
        public bool CanStackWith(ItemObject otherItem, out bool thisItemWillBeRemoved)
        {
            thisItemWillBeRemoved = false;
            
            if (this == otherItem) return false;
            if (!otherItem) return false;
            if (itemData.uniqueId != otherItem.itemData.uniqueId) return false;
            if (!itemData.stackable) return false;
            if (!otherItem.itemData.stackable) return false;
            
            if (itemData.stackSize + otherItem.itemData.stackSize > itemData.stackSizeMax) thisItemWillBeRemoved = true;
            return true;
        }

        public void StackWith(ItemObject otherItem) => StackWith(otherItem, out _);
        public void StackWith(ItemObject otherItem, out bool thisItemWillBeRemoved)
        {
            if (this == otherItem)
                throw new Exception("Trying to stack with itself!");
            
            if (!otherItem)
                throw new Exception("Other item doesn't exist!");

            if (itemData.uniqueId != otherItem.itemData.uniqueId)
                throw new Exception("Trying to stack with item having different unique id!");

            if (!itemData.stackable)
                throw new Exception("This item is not stackable!");

            if (!otherItem.itemData.stackable)
                throw new Exception("other item is not stackable!");

            var sum = itemData.stackSize + otherItem.itemData.stackSize;
            var overflow = sum - itemData.stackSizeMax;
            
            if (overflow > 0)
            {
                thisItemWillBeRemoved = false;
                otherItem.itemData.stackSize = itemData.stackSizeMax;
                itemData.stackSize = overflow;
            }
            else
            {
                thisItemWillBeRemoved = true;
                otherItem.itemData.stackSize = sum;
                Remove();
            }
        }
    }
}
