// Copyright Pacificao. All Rights Reserved.

#include "AgrarianResourceNode.h"
#include "AgrarianGameCharacter.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianItemDefinitionAsset.h"
#include "AgrarianSaveGame.h"
#include "Components/StaticMeshComponent.h"
#include "TimerManager.h"
#include "Net/UnrealNetwork.h"

AAgrarianResourceNode::AAgrarianResourceNode()
{
	bReplicates = true;
	NetCullDistanceSquared = FMath::Square(4500.0f);

	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	RootComponent = Mesh;
	Mesh->SetCollisionProfileName(TEXT("BlockAll"));

	YieldItem.ItemId = TEXT("wood");
	YieldItem.DisplayName = FText::FromString(TEXT("Wood"));
	YieldItem.Quantity = 1;
	YieldItem.UnitWeight = 1.0f;
}

void AAgrarianResourceNode::BeginPlay()
{
	Super::BeginPlay();

	if (HasAuthority())
	{
		MaxHarvests = FMath::Max(1, MaxHarvests);
		if (RemainingHarvests > MaxHarvests)
		{
			MaxHarvests = RemainingHarvests;
		}
	}

	UpdateDepletedState();
	ScheduleRespawnIfNeeded();
}

void AAgrarianResourceNode::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(AAgrarianResourceNode, RemainingHarvests);
}

FText AAgrarianResourceNode::GetInteractionText_Implementation(const AAgrarianGameCharacter* Interactor) const
{
	if (RemainingHarvests <= 0)
	{
		return FText::FromString(TEXT("Depleted"));
	}

	if (RequiredToolItemId != NAME_None && !HasRequiredTool(Interactor) && bAllowBareHandGathering)
	{
		return FText::FromString(TEXT("Gather by hand"));
	}

	return FText::FromString(TEXT("Gather"));
}

bool AAgrarianResourceNode::CanInteract_Implementation(const AAgrarianGameCharacter* Interactor) const
{
	if (RemainingHarvests <= 0 || !Interactor)
	{
		return false;
	}

	return RequiredToolItemId == NAME_None || bAllowBareHandGathering || HasRequiredTool(Interactor);
}

void AAgrarianResourceNode::Interact_Implementation(AAgrarianGameCharacter* Interactor)
{
	if (!HasAuthority() || !Interactor || RemainingHarvests <= 0)
	{
		return;
	}

	if (UAgrarianInventoryComponent* Inventory = Interactor->GetInventoryComponent())
	{
		const FAgrarianItemStack Granted = MakeYieldStack(Interactor);
		if (Inventory->AddItem(Granted))
		{
			RemainingHarvests--;
			UpdateDepletedState();
			ScheduleRespawnIfNeeded();
		}
	}
}

void AAgrarianResourceNode::OnRep_RemainingHarvests()
{
	UpdateDepletedState();
}

FName AAgrarianResourceNode::GetResourcePersistenceId() const
{
	return PersistenceNodeId != NAME_None ? PersistenceNodeId : GetFName();
}

FAgrarianSavedResourceNode AAgrarianResourceNode::CaptureResourceSaveState() const
{
	FAgrarianSavedResourceNode SavedNode;
	SavedNode.ResourceNodeId = GetResourcePersistenceId();
	SavedNode.RemainingHarvests = RemainingHarvests;
	SavedNode.bRespawnsForMvp = bRespawnsForMvp;
	return SavedNode;
}

void AAgrarianResourceNode::ApplyResourceSaveState(const FAgrarianSavedResourceNode& SavedNode)
{
	if (!HasAuthority() || SavedNode.ResourceNodeId == NAME_None || SavedNode.ResourceNodeId != GetResourcePersistenceId())
	{
		return;
	}

	RemainingHarvests = FMath::Clamp(SavedNode.RemainingHarvests, 0, FMath::Max(1, MaxHarvests));
	if (RemainingHarvests > 0)
	{
		if (UWorld* World = GetWorld())
		{
			World->GetTimerManager().ClearTimer(RespawnTimerHandle);
		}
	}

	UpdateDepletedState();
	ScheduleRespawnIfNeeded();
}

bool AAgrarianResourceNode::HasRequiredTool(const AAgrarianGameCharacter* Interactor) const
{
	if (RequiredToolItemId == NAME_None)
	{
		return true;
	}

	const UAgrarianInventoryComponent* Inventory = Interactor ? Interactor->GetInventoryComponent() : nullptr;
	return Inventory && Inventory->HasItem(RequiredToolItemId, 1);
}

int32 AAgrarianResourceNode::GetHarvestQuantityFor(const AAgrarianGameCharacter* Interactor) const
{
	const int32 ToolBonus = HasRequiredTool(Interactor) ? FMath::Max(0, ToolQuantityBonus) : 0;
	return FMath::Max(1, QuantityPerHarvest + ToolBonus);
}

FAgrarianItemStack AAgrarianResourceNode::MakeYieldStack(const AAgrarianGameCharacter* Interactor) const
{
	const int32 HarvestQuantity = GetHarvestQuantityFor(Interactor);
	if (YieldItemDefinition)
	{
		return YieldItemDefinition->MakeStack(HarvestQuantity);
	}

	FAgrarianItemStack Granted = YieldItem;
	Granted.Quantity = HarvestQuantity;
	return Granted;
}

void AAgrarianResourceNode::UpdateDepletedState()
{
	if (Mesh)
	{
		Mesh->SetVisibility(RemainingHarvests > 0, true);
		Mesh->SetCollisionEnabled(RemainingHarvests > 0 ? ECollisionEnabled::QueryAndPhysics : ECollisionEnabled::NoCollision);
	}
}

void AAgrarianResourceNode::ScheduleRespawnIfNeeded()
{
	if (!HasAuthority() || !bRespawnsForMvp || RemainingHarvests > 0)
	{
		return;
	}

	UWorld* World = GetWorld();
	if (!World || World->GetTimerManager().IsTimerActive(RespawnTimerHandle))
	{
		return;
	}

	World->GetTimerManager().SetTimer(
		RespawnTimerHandle,
		this,
		&AAgrarianResourceNode::RespawnNode,
		FMath::Max(1.0f, RespawnDelaySeconds),
		false);
}

void AAgrarianResourceNode::RespawnNode()
{
	if (!HasAuthority() || !bRespawnsForMvp)
	{
		return;
	}

	RemainingHarvests = FMath::Max(1, MaxHarvests);
	UpdateDepletedState();
}
