// Copyright ExByte Studios. Item Interaction Ecosystem.

#include "ItemCraftingLibrary.h"
#include "ItemInventoryLog.h"
#include "InventoryComponent.h"
#include "ItemDatabaseSubsystem.h"
#include "ItemContentRows.h"

bool UItemCraftingLibrary::CanCraft(const UObject* WorldContext, UInventoryComponent* Inventory, FName RecipeId, FGameplayTag AvailableStation)
{
	const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
	if (!DB || !Inventory)
	{
		return false;
	}
	FCraftingRecipeRow Recipe;
	if (!DB->FindRecipe(RecipeId, Recipe))
	{
		return false;
	}
	if (Recipe.StationTag.IsValid() && !AvailableStation.MatchesTag(Recipe.StationTag))
	{
		return false;
	}
	for (const FCraftingIngredient& In : Recipe.Inputs)
	{
		if (Inventory->GetItemCount(In.ItemId) < In.Quantity)
		{
			return false;
		}
	}
	return true;
}

bool UItemCraftingLibrary::Craft(const UObject* WorldContext, UInventoryComponent* Inventory, FName RecipeId, FGameplayTag AvailableStation)
{
	if (!Inventory || !Inventory->GetOwner() || !Inventory->GetOwner()->HasAuthority())
	{
		return false;
	}
	if (!CanCraft(WorldContext, Inventory, RecipeId, AvailableStation))
	{
		return false;
	}

	const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
	FCraftingRecipeRow Recipe;
	DB->FindRecipe(RecipeId, Recipe);

	// Consume inputs (already verified present).
	for (const FCraftingIngredient& In : Recipe.Inputs)
	{
		if (!ConsumeItem(Inventory, In.ItemId, In.Quantity))
		{
			UE_LOG(LogItemInventory, Error, TEXT("Craft '%s' failed mid-consume for %s."), *RecipeId.ToString(), *In.ItemId.ToString());
			return false;
		}
	}

	FItemInstanceData Output = DB->MakeItemInstance(Recipe.OutputItemId, FMath::Max(1, Recipe.OutputQuantity));
	Inventory->AddItem(Output);
	UE_LOG(LogItemInventory, Log, TEXT("[Crafting] crafted %s"), *Recipe.OutputItemId.ToString());
	return true;
}

void UItemCraftingLibrary::RollLootIntoInventory(const UObject* WorldContext, UInventoryComponent* Inventory, FName TableId, int32 RollCount)
{
	const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
	if (!DB || !Inventory || !Inventory->GetOwner() || !Inventory->GetOwner()->HasAuthority())
	{
		return;
	}
	TArray<FItemInstanceData> Loot;
	DB->RollLoot(TableId, RollCount, Loot);
	for (FItemInstanceData& Item : Loot)
	{
		Inventory->AddItem(Item);
	}
}

bool UItemCraftingLibrary::ConsumeItem(UInventoryComponent* Inventory, FName ItemId, int32 Count)
{
	int32 Remaining = Count;
	TArray<FItemInstanceData> Items;
	Inventory->GetAllItems(Items);
	for (const FItemInstanceData& Item : Items)
	{
		if (Remaining <= 0) { break; }
		if (Item.ItemId != ItemId || Item.Lock.bLocked) { continue; }

		const int32 Take = FMath::Min(Remaining, Item.Quantity);
		if (Inventory->RemoveQuantity(Item.InstanceId, Take))
		{
			Remaining -= Take;
		}
	}
	return Remaining <= 0;
}
