// © 2025 Naked People Team. All Rights Reserved.


#include "Bed.h"

#include "Components/BoxComponent.h"
#include "Components/WidgetComponent.h"
#include "NakedDesire/Global/SessionLossResolver.h"
#include "NakedDesire/Global/SessionManagerSubsystem.h"
#include "NakedDesire/Global/TimeOfDaySubsystem.h"

#define LOCTEXT_NAMESPACE "Bed"

ABed::ABed()
{
	ColliderComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Collider"));
	SetRootComponent(ColliderComponent);
	// Trace-only: detected by the interaction LOS/focus line traces (ECC_Visibility),
	// transparent to movement and physics.
	ColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	ColliderComponent->SetCollisionObjectType(ECC_WorldStatic);
	ColliderComponent->SetCollisionResponseToAllChannels(ECR_Ignore);
	ColliderComponent->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);

	MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	MeshComponent->SetupAttachment(RootComponent);
	// Movement blocker only: stops the character capsule (ECC_Pawn), but is invisible
	// to line traces so it never occludes the interaction LOS check.
	MeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	MeshComponent->SetCollisionObjectType(ECC_WorldStatic);
	MeshComponent->SetCollisionResponseToAllChannels(ECR_Ignore);
	MeshComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);

	InteractionHint = CreateDefaultSubobject<UWidgetComponent>(TEXT("Interaction Hint"));
	InteractionHint->SetupAttachment(RootComponent);
}

void ABed::Interact_Implementation(ANakedDesireCharacter* Player)
{
	// Cosmetic transition first (BP), then resolve the sleep. The skip is instantaneous,
	// so a BP fade simply overlaps it and clears on the new time.
	PlaySleepTransition();
	DoSleep();
}

bool ABed::CanInteract_Implementation(ANakedDesireCharacter* Player) const
{
	// The bed only exists in the apartment, so reaching it means the session has ended.
	// Guard defensively against sleeping while a session is still flagged active.
	if (const UWorld* World = GetWorld())
	{
		if (const USessionManagerSubsystem* Session = World->GetSubsystem<USessionManagerSubsystem>())
		{
			return !Session->IsSessionActive();
		}
	}
	return true;
}

FText ABed::GetInteractionPrompt_Implementation() const
{
	return LOCTEXT("SleepPrompt", "Sleep");
}

void ABed::DoSleep()
{
	UWorld* World = GetWorld();
	if (!World)
		return;

	// §4.4 step 2: clothing left outside the apartment is guaranteed lost on sleep. The
	// loss is owned by USessionLossResolver (all "what gets lost" logic lives there).
	if (USessionLossResolver* Resolver = World->GetSubsystem<USessionLossResolver>())
	{
		Resolver->ResolveSleepLoss();
	}

	// Time skip + energy restore + phone charge + autosave (§2.4 / §9.8 / §11.19). This
	// is the single save for the whole sleep flow — it also persists the loss above.
	if (UTimeOfDaySubsystem* Time = World->GetSubsystem<UTimeOfDaySubsystem>())
	{
		Time->Sleep();
	}
}

void ABed::HideInteractionHint_Implementation()
{
	ApplyOutline(false, 0);
	InteractionHint->SetVisibility(false);
}

void ABed::ShowInteractionFocusHint_Implementation()
{
	ApplyOutline(true, 2);
	InteractionHint->SetVisibility(true);
}

void ABed::ShowInteractionProximityHint_Implementation()
{
	ApplyOutline(true, 1);
	InteractionHint->SetVisibility(false);
}

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

	InteractionHint->SetVisibility(false);
}

void ABed::ApplyOutline(bool bEnabled, int32 StencilValue)
{
	MeshComponent->SetRenderCustomDepth(bEnabled);
	MeshComponent->SetCustomDepthStencilValue(StencilValue);
}

#undef LOCTEXT_NAMESPACE
