// Copyright ExByte Studios. Item Interaction Ecosystem.

#include "WorldItemSlotComponent.h"
#include "PickupItemActor.h"
#include "ItemWorldLog.h"
#include "ItemDatabaseSubsystem.h"
#include "ItemDefinitionRow.h"
#include "Net/UnrealNetwork.h"

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

void UWorldItemSlotComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(UWorldItemSlotComponent, InstalledItem);
	DOREPLIFETIME(UWorldItemSlotComponent, InstalledVisual);
}

bool UWorldItemSlotComponent::HasAuthority() const
{
	return GetOwner() && GetOwner()->HasAuthority();
}

USceneComponent* UWorldItemSlotComponent::GetAttachTarget() const
{
	return GetOwner() ? GetOwner()->GetRootComponent() : nullptr;
}

bool UWorldItemSlotComponent::CanInstall(const FItemInstanceData& Item) const
{
	if (InstalledItem.bHasItem || !Item.IsValid())
	{
		return false;
	}
	const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(this);
	const FItemDefinitionRow* Def = DB ? DB->GetItemDefinition(Item.ItemId) : nullptr;
	if (!Def || !Def->bCanInstallIntoWorldSlot)
	{
		return false;
	}
	// The item must declare this slot as compatible.
	if (!Def->CompatibleWorldSlots.HasTag(SlotTag))
	{
		return false;
	}
	// Optional extra tag gate.
	if (!AcceptedItemTags.IsEmpty())
	{
		FGameplayTagContainer ItemTags = Def->ItemTags;
		ItemTags.AddTag(Def->ItemType);
		if (!ItemTags.HasAny(AcceptedItemTags))
		{
			return false;
		}
	}
	return true;
}

bool UWorldItemSlotComponent::InstallItem(const FItemInstanceData& Item, AActor* ProvidedVisual)
{
	if (!HasAuthority() || !CanInstall(Item))
	{
		return false;
	}

	InstalledItem.bHasItem = true;
	InstalledItem.Item = Item;
	InstalledItem.Item.LocationType = EItemLocationType::Installed;

	if (ProvidedVisual)
	{
		// Reuse the carried actor as the visual.
		if (USceneComponent* Target = GetAttachTarget())
		{
			ProvidedVisual->AttachToComponent(Target, FAttachmentTransformRules::SnapToTargetIncludingScale, AttachSocketName);
		}
		if (IWorldItemProvider* Provider = Cast<IWorldItemProvider>(ProvidedVisual))
		{
			Provider->SetCarried(GetOwner(), true); // keep collision off, locked
		}
		InstalledVisual = ProvidedVisual;
	}
	else
	{
		// Spawn a fresh visual pickup and attach it.
		const FTransform Xf = GetAttachTarget() ? GetAttachTarget()->GetComponentTransform() : GetOwner()->GetActorTransform();
		if (APickupItemActor* Visual = APickupItemActor::SpawnPickupFromInstance(this, Item, Xf))
		{
			Visual->SetCarried(GetOwner(), true);
			if (USceneComponent* Target = GetAttachTarget())
			{
				Visual->AttachToComponent(Target, FAttachmentTransformRules::SnapToTargetIncludingScale, AttachSocketName);
			}
			InstalledVisual = Visual;
		}
	}

	OnSlotChanged.Broadcast();
	UE_LOG(LogItemWorld, Log, TEXT("[Interaction] installed %s into %s"), *Item.ItemId.ToString(), *SlotTag.ToString());
	return true;
}

bool UWorldItemSlotComponent::RemoveItem(FItemInstanceData& OutItem)
{
	if (!HasAuthority() || !InstalledItem.bHasItem)
	{
		return false;
	}
	OutItem = InstalledItem.Item;

	if (InstalledVisual)
	{
		InstalledVisual->Destroy();
		InstalledVisual = nullptr;
	}

	InstalledItem = FWorldSlotInstalledState();
	OnSlotChanged.Broadcast();
	return true;
}

void UWorldItemSlotComponent::OnRep_InstalledItem()
{
	OnSlotChanged.Broadcast();
}
