// Copyright ExByte Studios. Item Interaction Ecosystem.

#pragma once

#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "ItemEnums.h"
#include "ItemRuntimeProperties.h"
#include "ItemLockData.h"
#include "ItemInstanceData.generated.h"

/**
 * Runtime representation of a single item instance (section 4.3).
 *
 * Deliberately tiny: it never copies the static FItemDefinitionRow - it only
 * stores the stable ItemId plus this instance's mutable state. Resolve the
 * definition through UItemDatabaseSubsystem when you need icons/meshes/flags.
 */
USTRUCT(BlueprintType)
struct ITEMCORE_API FItemInstanceData
{
	GENERATED_BODY()

	/** Globally unique handle for this concrete stack/instance. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	FGuid InstanceId;

	/** Stable design-time id (e.g. "ammo_9mm"). Never a localized string. */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	FName ItemId;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	int32 Quantity = 1;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	FGameplayTagContainer StateTags;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	FItemRuntimeProperties RuntimeProperties;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	FGuid CurrentContainerId;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	int32 SlotIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	EItemLocationType LocationType = EItemLocationType::None;

	/** Bumped when the item schema changes; drives save migration (section 26.2). */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item")
	int32 ItemDataVersion = 1;

	/** Transient server-side lock; not saved (section 20). */
	UPROPERTY(BlueprintReadOnly, Transient, Category = "Item")
	FItemLockData Lock;

	FItemInstanceData() = default;

	explicit FItemInstanceData(FName InItemId, int32 InQuantity = 1)
		: InstanceId(FGuid::NewGuid())
		, ItemId(InItemId)
		, Quantity(InQuantity)
	{
	}

	bool IsValid() const { return !ItemId.IsNone() && InstanceId.IsValid() && Quantity > 0; }

	void EnsureInstanceId()
	{
		if (!InstanceId.IsValid())
		{
			InstanceId = FGuid::NewGuid();
		}
	}

	/**
	 * Two instances can merge into one stack only when they are the same item,
	 * carry identical per-instance state and neither is locked. MaxStack is
	 * enforced by the inventory layer which has access to the definition.
	 */
	bool CanStackWith(const FItemInstanceData& Other) const
	{
		return ItemId == Other.ItemId
			&& !Lock.bLocked && !Other.Lock.bLocked
			&& StateTags == Other.StateTags
			&& RuntimeProperties == Other.RuntimeProperties;
	}

	bool operator==(const FItemInstanceData& Other) const { return InstanceId == Other.InstanceId; }
	bool operator!=(const FItemInstanceData& Other) const { return InstanceId != Other.InstanceId; }
};
