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


#include "SessionLossResolver.h"

#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Interactables/ItemPickup.h"
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
#include "NakedDesire/SaveGame/SaveSubsystem.h"

void USessionLossResolver::OnWorldBeginPlay(UWorld& InWorld)
{
	Super::OnWorldBeginPlay(InWorld);

	if (USessionManagerSubsystem* SessionManager = InWorld.GetSubsystem<USessionManagerSubsystem>())
	{
		SessionManager->OnSessionEnd.AddDynamic(this, &USessionLossResolver::ResolveLoss);
	}
}

void USessionLossResolver::ResolveLoss(ESessionLossCause Cause)
{
	// §4.4 precedence: if a chase is in progress, the loss always resolves as
	// police capture — the chase wins regardless of what actually fired.
	if (const USessionManagerSubsystem* SessionManager = GetWorld()->GetSubsystem<USessionManagerSubsystem>())
	{
		if (SessionManager->IsPoliceChaseActive())
		{
			Cause = ESessionLossCause::PoliceCapture;
		}
	}

	// §4.4: equipped clothing is never forcibly removed — the resolver simply never
	// touches equipped items.
	// TODO(§6.4): when bags exist, a bag placed in the world is lost with its
	// contents — mark its world record for deletion here.

	bool bWentToHoldingCell = false;

	switch (Cause)
	{
	case ESessionLossCause::SafeReturn:
		// Normal session end; nothing is lost. Autosave below.
		break;

	case ESessionLossCause::EmbarrassmentMax:
		// Fade to apartment with no extra cost — no time skip, money, or rep hit.
		break;

	case ESessionLossCause::EnergyZero:
		// Forced sleep cycle: everything left outside the apartment is guaranteed lost.
		LoseAllWorldClothing();
		break;

	case ESessionLossCause::PoliceCapture:
		{
			UGlobalSaveGameData* Save = GetSave();
			if (Save && Save->Money >= PoliceCaptureMoneyPenalty)
			{
				// Can pay: deduct the penalty and go home.
				Save->Money -= PoliceCaptureMoneyPenalty;
			}
			else
			{
				// Can't pay: a night in the holding cell settles the debt; no money owed.
				bWentToHoldingCell = true;
			}
			ClearWanted();
		}
		break;
	}

	Autosave();

	OnSessionLossResolved.Broadcast(Cause, bWentToHoldingCell);
}

void USessionLossResolver::ResolveSleepLoss()
{
	// §4.4: sleeping is the deterministic side of the theft model — everything left
	// outside the apartment is lost, regardless of the in-session grace / roll state.
	// No autosave here: the sleep flow persists once via UTimeOfDaySubsystem::Sleep(),
	// which runs immediately after this. Callers must ensure a save follows.
	LoseAllWorldClothing();
}

void USessionLossResolver::LoseAllWorldClothing()
{
	UGlobalSaveGameData* Save = GetSave();

	// Loose clothing in the world is represented by AItemPickup actors. Dropping only
	// happens outside, and the apartment stores items in the wardrobe (not as pickups),
	// so every pickup currently counts as "outside the apartment".
	// TODO: once an apartment volume query exists, spare pickups that sit inside it.
	TArray<AActor*> Pickups;
	UGameplayStatics::GetAllActorsOfClass(this, AItemPickup::StaticClass(), Pickups);
	for (AActor* Actor : Pickups)
	{
		AItemPickup* Pickup = Cast<AItemPickup>(Actor);
		if (!Pickup)
			continue;

		if (Save)
		{
			if (UClothingItemInstance* Item = Pickup->GetItem())
			{
				Save->RemoveWorldItem(Item);
			}
		}
		Pickup->Destroy();
	}
}

void USessionLossResolver::ClearWanted()
{
	// TODO(§7.7 / Phase 6): clear the `wanted` tag once the Wanted attribute exists.
}

void USessionLossResolver::Autosave() const
{
	if (const UGameInstance* GameInstance = GetWorld()->GetGameInstance())
	{
		if (USaveSubsystem* SaveSubsystem = GameInstance->GetSubsystem<USaveSubsystem>())
		{
			SaveSubsystem->SaveGame();
		}
	}
}

UGlobalSaveGameData* USessionLossResolver::GetSave() const
{
	if (const UGameInstance* GameInstance = GetWorld()->GetGameInstance())
	{
		if (USaveSubsystem* SaveSubsystem = GameInstance->GetSubsystem<USaveSubsystem>())
		{
			return SaveSubsystem->GetCurrentSave();
		}
	}
	return nullptr;
}