namespace Benim.InventorySystem
{
    public class SlotItem
    {
        internal Item Item { get; private set; } = null;
        internal int Quantity { get; private set; } = 0;
        internal bool IsEmpty => Quantity <= 0;

        public SlotItem(Item item, int quantity)
        {
            Item = item;
            Quantity = quantity;
        }

        internal void AddItem(int quantity)
        {
            Quantity += quantity;
        }

        internal void RemoveItem(int quantity)
        {
            Quantity -= quantity;
        }
    }
}

using System;
using System.Collections;
using UnityEngine;

namespace Benim.InventorySystem
{
    public class Slot : MonoBehaviour, ISlot
    {
        [SerializeField] private GameObject itemHolderPrefab;
        [SerializeField] private ItemType allowedItemTypes;

        [SerializeField] private GameObject itemHolderInstance;
        [SerializeField] private SlotItem slotItem;

        public GameObject ItemHolderInstance => itemHolderInstance;
        public SlotItem SlotItem => slotItem;
        public SlotItem TempSlotItem { get; set; }

        public bool IsEmpty => slotItem == null || slotItem.IsEmpty;
        public bool IsSlotFull => !IsEmpty && slotItem.Quantity >= slotItem.Item.MaxStackSize;

        public event Action OnItemAdded;
        public event Action OnItemRemoved;
        public event Action OnSlotChanged;

        private bool CanCombine(SlotItem other) => !other.IsEmpty &&
                                                    other.Item == slotItem.Item &&
                                                    slotItem.Item.IsStackable &&
                                                    slotItem.Item.MaxStackSize > slotItem.Quantity;

        public TransferResult AddItemToSlot(SlotItem other)
        {
            if (!IsItemAllowed(other.Item)) return TransferResult.Failed;
            var result = IsEmpty ? CreateAndAddItem(other) : CombineItem(other);
            if (result != TransferResult.Failed) StartCoroutine(InvokeItemAdded());
            return result;
        }

        private TransferResult CreateAndAddItem(SlotItem other)
        {
            if (other.IsEmpty) return TransferResult.Failed;

            int transferAmount = Mathf.Min(other.Quantity, other.Item.MaxStackSize);
            var newSlotItem = new SlotItem(other.Item, transferAmount);
            var newHolder = Instantiate(itemHolderPrefab, transform);

            other.RemoveItem(transferAmount);
            AssingSlotReferences(newHolder, newSlotItem);
            return TransferResult.CreatedAndAdded;
        }

        private TransferResult CombineItem(SlotItem other)
        {
            if (!CanCombine(other)) return TransferResult.Failed;

            int transferAmount = Mathf.Min(other.Quantity, slotItem.Item.MaxStackSize - slotItem.Quantity);
            other.RemoveItem(transferAmount);
            slotItem.AddItem(transferAmount);
            return other.Quantity == 0 ? TransferResult.AllCombined : TransferResult.PartiallyCombined;
        }

        public TransferResult TransferItemWithinSlots(GameObject otherItemHolder, SlotItem other)
        {
            if (!IsItemAllowed(other.Item)) return TransferResult.Failed;

            TransferResult result = IsEmpty ? AddItemToEmpty(otherItemHolder, other) : CombineItem(other);
            if (result == TransferResult.Failed) result = SwapItem(otherItemHolder, other);
            StartCoroutine(InvokeItemAdded());

            var otherSlot = otherItemHolder.transform.parent.GetComponent<Slot>();
            if (otherSlot != null)
            {
                Debug.Log("asd");
                otherSlot.StartCoroutine(InvokeItemRemoved());
            }

            return result;
        }

        private TransferResult AddItemToEmpty(GameObject otherItemHolder, SlotItem other)
        {
            return TransferResult.AddedToEmpty;
        }

        private TransferResult SwapItem(GameObject otherItemHolder, SlotItem other)
        {
            return TransferResult.Swapped;
        }

        public TransferResult RemoveItemFromSlot(int quantity)
        {
            if (IsEmpty) return TransferResult.Failed;

            slotItem.RemoveItem(quantity);
            StartCoroutine(InvokeItemRemoved());
            return TransferResult.Removed;
        }

        public void AssingSlotReferences(GameObject itemHolder, SlotItem slotItem)
        {
            itemHolderInstance = itemHolder;
            this.slotItem = slotItem;
        }

        public TransferResult SplitItem(int splitQuantity, out SlotItem newSlotItem)
        {
            newSlotItem = null;

            if (IsEmpty || splitQuantity <= 0 || splitQuantity >= slotItem.Quantity)
            {
                Debug.LogWarning("Invalid split operation.");
                return TransferResult.Failed;
            }

            newSlotItem = new SlotItem(slotItem.Item, splitQuantity);
            slotItem.RemoveItem(splitQuantity);
            return TransferResult.Split;
        }

        public bool IsItemAllowed(Item item)
        {
            return (allowedItemTypes & item.ItemType) != ItemType.None;
        }

        public void SetAllowedItemTypes(ItemType types)
        {
            allowedItemTypes = types;
        }

        private IEnumerator InvokeItemAdded()
        {
            yield return new WaitForEndOfFrame();
            OnItemAdded?.Invoke();
            OnSlotChanged?.Invoke();
            Debug.Log(name + " InvokeItemAdded");
        }

        private IEnumerator InvokeItemRemoved()
        {
            yield return new WaitForEndOfFrame();
            OnItemRemoved?.Invoke();
            OnSlotChanged?.Invoke();
            Debug.Log(name + " InvokeItemRemoved");
        }
    }
}

Eventelerin metotlardan tetiklenmesi hoşuma gitmiyor direk slot classındaki slotitem prop undan tetiklenebilir ama sadece set ile değil get ile AddItem kullanarak da değişim olabiliyor onuda göz önünde tutmak lazım