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


#include "NPCAIController.h"
#include "NPC.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Perception/AISense_Sight.h"
#include "Perception/AISenseConfig_Sight.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#include "NakedDesire/Stats/StatsManager.h"
#include "Perception/AIPerceptionComponent.h"

ANPCAIController::ANPCAIController()
{
	UAIPerceptionComponent* Perception = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("PerceptionComponent"));
	SetPerceptionComponent(*Perception);

	UAISenseConfig_Sight* SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SightConfig"));
	SightConfig->DetectionByAffiliation.bDetectEnemies = true;
	SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
	SightConfig->DetectionByAffiliation.bDetectFriendlies = true;
	SightConfig->SightRadius = 2000.0f;
	SightConfig->LoseSightRadius = 2500.0f;
	Perception->ConfigureSense(*SightConfig);
	Perception->SetDominantSense(SightConfig->GetSenseImplementation());
}

void ANPCAIController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);

	PerceptionComponent->OnTargetPerceptionUpdated.AddUniqueDynamic(this, &ANPCAIController::OnTargetPerceptionUpdate);
}

void ANPCAIController::OnUnPossess()
{
	ClearObservation();
	Super::OnUnPossess();
}

void ANPCAIController::ClearObservation()
{
	if (bCurrentlyObserving && PlayerCharacter)
	{
		PlayerCharacter->StatsManager->SetObserved(false, GetPawn());
		bCurrentlyObserving = false;
	}
}

void ANPCAIController::RunCustomBehaviorTree()
{
	RunBehaviorTree(BehaviorTreeAsset);
}

float ANPCAIController::GetObservationWeight() const
{
	if (const ANPC* NPC = Cast<ANPC>(GetPawn()))
		return NPC->GetObservationWeight();
	return 1.0f;
}

void ANPCAIController::OnTargetPerceptionUpdate(AActor* Actor, FAIStimulus Stimulus)
{
	if (Stimulus.Type != UAISense::GetSenseID<UAISense_Sight>())
		return;

	ANakedDesireCharacter* SensedPlayer = Cast<ANakedDesireCharacter>(Actor);
	if (!SensedPlayer)
		return;

	PlayerCharacter = SensedPlayer;

	const bool bSensed = Stimulus.WasSuccessfullySensed();
	if (bSensed == bCurrentlyObserving)
		return;

	bCurrentlyObserving = bSensed;
	PlayerCharacter->StatsManager->SetObserved(bSensed, GetPawn(), GetObservationWeight());

	// Surface the sighting to the behavior tree: the react/wander branch keys off "Player", and a
	// fresh sighting re-arms the one-shot notice reaction (see the BT handoff in PLAN.md).
	if (UBlackboardComponent* BB = GetBlackboardComponent())
	{
		BB->SetValueAsObject(TEXT("Player"), bSensed ? PlayerCharacter : nullptr);
		if (!bSensed)
			BB->SetValueAsBool(TEXT("bHasReacted"), false);
	}
}
