// Copyright Pacificao. All Rights Reserved.

#include "AgrarianCraftingComponent.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianRecipeDataAsset.h"

UAgrarianCraftingComponent::UAgrarianCraftingComponent()
{
	PrimaryComponentTick.bCanEverTick = false;
	SetIsReplicatedByDefault(true);
}

bool UAgrarianCraftingComponent::CanCraft(FName RecipeId, FText& FailureReason) const
{
	FAgrarianRecipe Recipe;
	if (!FindRecipe(RecipeId, Recipe))
	{
		FailureReason = FText::FromString(TEXT("Recipe is not known."));
		return false;
	}

	const UAgrarianInventoryComponent* Inventory = GetInventory();
	if (!Inventory)
	{
		FailureReason = FText::FromString(TEXT("No inventory is available."));
		return false;
	}

	for (const FAgrarianItemStack& Ingredient : Recipe.Ingredients)
	{
		if (!Inventory->HasItem(Ingredient.ItemId, Ingredient.Quantity))
		{
			FailureReason = FText::Format(
				FText::FromString(TEXT("Missing ingredient: {0}")),
				Ingredient.DisplayName.IsEmpty() ? FText::FromName(Ingredient.ItemId) : Ingredient.DisplayName);
			return false;
		}
	}

	return true;
}

bool UAgrarianCraftingComponent::Craft(FName RecipeId)
{
	if (!GetOwner())
	{
		return false;
	}

	if (!GetOwner()->HasAuthority())
	{
		ServerCraft(RecipeId);
		return true;
	}

	FText FailureReason;
	if (!CanCraft(RecipeId, FailureReason))
	{
		FailCraft(RecipeId, FailureReason);
		return false;
	}

	FAgrarianRecipe Recipe;
	FindRecipe(RecipeId, Recipe);

	UAgrarianInventoryComponent* Inventory = GetInventory();
	if (!Inventory)
	{
		FailCraft(RecipeId, FText::FromString(TEXT("No inventory is available.")));
		return false;
	}

	for (const FAgrarianItemStack& Ingredient : Recipe.Ingredients)
	{
		Inventory->RemoveItem(Ingredient.ItemId, Ingredient.Quantity);
	}

	if (!Inventory->AddItem(Recipe.Result))
	{
		FailCraft(RecipeId, FText::FromString(TEXT("Inventory is full.")));
		return false;
	}

	OnCraftCompleted.Broadcast(Recipe.RecipeId, Recipe.Result);
	return true;
}

void UAgrarianCraftingComponent::ServerCraft_Implementation(FName RecipeId)
{
	Craft(RecipeId);
}

bool UAgrarianCraftingComponent::AddKnownRecipe(const FAgrarianRecipe& Recipe)
{
	if (Recipe.RecipeId == NAME_None || !Recipe.Result.IsValidStack())
	{
		return false;
	}

	for (FAgrarianRecipe& Existing : KnownRecipes)
	{
		if (Existing.RecipeId == Recipe.RecipeId)
		{
			Existing = Recipe;
			return true;
		}
	}

	KnownRecipes.Add(Recipe);
	return true;
}

bool UAgrarianCraftingComponent::FindRecipe(FName RecipeId, FAgrarianRecipe& OutRecipe) const
{
	for (const UAgrarianRecipeDataAsset* RecipeAsset : KnownRecipeAssets)
	{
		if (RecipeAsset && RecipeAsset->Recipe.RecipeId == RecipeId)
		{
			OutRecipe = RecipeAsset->Recipe;
			return true;
		}
	}

	for (const FAgrarianRecipe& Recipe : KnownRecipes)
	{
		if (Recipe.RecipeId == RecipeId)
		{
			OutRecipe = Recipe;
			return true;
		}
	}

	return false;
}

void UAgrarianCraftingComponent::GetKnownRecipes(TArray<FAgrarianRecipe>& OutRecipes) const
{
	OutRecipes.Reset();

	for (const UAgrarianRecipeDataAsset* RecipeAsset : KnownRecipeAssets)
	{
		if (RecipeAsset && RecipeAsset->Recipe.RecipeId != NAME_None)
		{
			OutRecipes.Add(RecipeAsset->Recipe);
		}
	}

	for (const FAgrarianRecipe& Recipe : KnownRecipes)
	{
		if (Recipe.RecipeId != NAME_None)
		{
			OutRecipes.Add(Recipe);
		}
	}
}

UAgrarianInventoryComponent* UAgrarianCraftingComponent::GetInventory() const
{
	return GetOwner() ? GetOwner()->FindComponentByClass<UAgrarianInventoryComponent>() : nullptr;
}

void UAgrarianCraftingComponent::FailCraft(FName RecipeId, const FText& Reason)
{
	OnCraftFailed.Broadcast(RecipeId, Reason);
}
