// Copyright ExByte Studios. Item Interaction Ecosystem.

#pragma once

#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "ItemInstanceData.h"
#include "ItemTransaction.generated.h"

class UInventoryComponent;

/** Kind of mutation a transaction step performs (section 10). */
UENUM(BlueprintType)
enum class EItemTransactionStepType : uint8
{
	AddItem,
	RemoveItem,
	UpdateItem,
	MoveItem
};

/** One atomic step. Carries enough to apply AND to roll back. */
USTRUCT(BlueprintType)
struct ITEMINVENTORY_API FItemTransactionStep
{
	GENERATED_BODY()

	UPROPERTY()
	EItemTransactionStepType Type = EItemTransactionStepType::AddItem;

	UPROPERTY()
	TWeakObjectPtr<UInventoryComponent> Inventory;

	/** Item payload for AddItem / UpdateItem. */
	UPROPERTY()
	FItemInstanceData Item;

	UPROPERTY()
	FGuid InstanceId;

	UPROPERTY()
	FGuid ToContainer;

	UPROPERTY()
	int32 ToSlot = INDEX_NONE;

	// --- runtime (rollback bookkeeping) ---
	UPROPERTY()
	bool bApplied = false;

	UPROPERTY()
	FItemInstanceData UndoSnapshot;

	UPROPERTY()
	FGuid UndoFromContainer;

	UPROPERTY()
	int32 UndoFromSlot = INDEX_NONE;
};

/**
 * Atomic multi-step item operation (section 10). Build steps, then Apply():
 * if any step fails, every applied step is rolled back so the inventories are
 * left untouched. The single correct way to do multi-inventory moves.
 *
 * Server-only. Operates through UInventoryComponent's authoritative methods.
 */
USTRUCT(BlueprintType)
struct ITEMINVENTORY_API FItemTransaction
{
	GENERATED_BODY()

	UPROPERTY()
	FGuid TransactionId;

	UPROPERTY()
	FGameplayTag TransactionType;

	UPROPERTY()
	TWeakObjectPtr<AActor> Instigator;

	UPROPERTY()
	TArray<FItemTransactionStep> Steps;

	FItemTransaction() { TransactionId = FGuid::NewGuid(); }

	// --- Builders ---
	FItemTransaction& Add(UInventoryComponent* Inventory, const FItemInstanceData& Item);
	FItemTransaction& Remove(UInventoryComponent* Inventory, const FGuid& InstanceId);
	FItemTransaction& Update(UInventoryComponent* Inventory, const FItemInstanceData& Item);
	FItemTransaction& Move(UInventoryComponent* Inventory, const FGuid& InstanceId, const FGuid& ToContainerId, int32 ToSlotIndex);

	/** Non-mutating feasibility check of every step. */
	bool Validate() const;

	/** Applies all steps; on any failure rolls back and returns false. */
	bool Apply();

	/** Undoes every applied step in reverse order. */
	void Rollback();
};
